Compare commits

...

376 Commits

Author SHA1 Message Date
Ankit Pandey b68b05c64c reader/processor: error out for improper rewrite regexp
It's possible to specify a rewrite regex that validates but doesn't compile such
as:

    rewrite("(((unmatched-capture-group"|"rewrite)))")

In case we encounter one, exit early instead of letting the server panic.
2024-06-01 10:37:02 -07:00
Frédéric Guillot 5ce3f24838 googelreader: set CrawlTimeMsec at the correct precision
Fixes #2669

Fixes #2670
2024-05-29 21:54:02 -07:00
dependabot[bot] 48ddc02ba8 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.30 to 2.20.32
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.30 to 2.20.32.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.30...v2.20.32)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 15:46:53 -07:00
dependabot[bot] fe9f1bba16 build(deps): bump library/alpine in /packaging/docker/alpine
Bumps library/alpine from 3.19 to 3.20.

---
updated-dependencies:
- dependency-name: library/alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 15:44:57 -07:00
Krish Mamtora 740fa4a5d2 Add missing properties when reloading page after error 2024-05-27 15:37:53 -07:00
dependabot[bot] 8a38f54ef5 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.25 to 2.20.30
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.25 to 2.20.30.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.25...v2.20.30)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-22 19:16:11 -07:00
Zhizhen He ae432bc9c6
reader/readingtime: fix incorrect package name 2024-05-21 18:12:24 -07:00
dependabot[bot] 96f7e8bae0 ---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 17:48:00 -07:00
rootknight 1f35ed1675
ui: add `viewport-fit=cover` 2024-05-19 10:39:34 -07:00
dependabot[bot] d6deac1810 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.21 to 2.20.24
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.21 to 2.20.24.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.21...v2.20.24)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-18 08:39:43 -07:00
Frédéric Guillot b692768730 packaging: fix failed to solve: arm64v8/golang:1.22-bookworm 2024-05-17 21:07:40 -07:00
dependabot[bot] 2178580a75 build(deps): bump golangci/golangci-lint-action from 5 to 6
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5 to 6.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 17:59:04 -07:00
dependabot[bot] b52f61cc77 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.20 to 2.20.21
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.20 to 2.20.21.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.20...v2.20.21)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 17:58:41 -07:00
dependabot[bot] 3388f8e376 Bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.19.0 to 1.19.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-09 21:37:53 -07:00
dependabot[bot] 83ceb20c1c Bump golang.org/x/net from 0.24.0 to 0.25.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/net/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 16:45:02 -07:00
dependabot[bot] c06850ca34 Bump golang.org/x/oauth2 from 0.19.0 to 0.20.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 16:22:00 -07:00
dependabot[bot] d856c02fbb Bump golang.org/x/crypto from 0.22.0 to 0.23.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 15:42:12 -07:00
Jan-Lukas Else a33b1adf13 Add description field to feed settings
This adds a new "description" field to the feed settings. This allows to
save custom description regarding a feed. It is also exported and
imported as "description" in OPML.
2024-05-06 15:40:36 -07:00
fin444 a631bd527d options: add FETCH_NEBULA_WATCH_TIME 2024-05-02 16:30:01 -07:00
Alpha Chen ca62b0b36b integration/raindrop: initial draft implementation 2024-05-02 16:23:00 -07:00
Kioubit 7d6a4243c1 Make cookie duration dependent on configuration
This ensures that session cookies are not expiring before the session is cleaned up from the database as per CLEANUP_REMOVE_SESSIONS_DAYS.
As of now the usefulness of this configuration option is diminished as extending it has no effect on the actual browser session due to the cookie expiry.
Fixes: #2214
2024-05-01 19:34:13 -07:00
dependabot[bot] d056aa1f73 Bump github.com/PuerkitoBio/goquery from 1.9.1 to 1.9.2
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.9.1 to 1.9.2.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.9.1...v1.9.2)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-30 17:23:35 -07:00
dependabot[bot] 018e24404e Bump golangci/golangci-lint-action from 4 to 5
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4 to 5.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 16:51:20 -07:00
Frédéric Guillot 4d3ee0d15d ci: fix docker workflow to add distroless suffix on latest tag 2024-04-27 15:26:16 -07:00
Frédéric Guillot 797450986b Update ChangeLog 2024-04-27 15:06:28 -07:00
Ztec 93bc9ce24d add seek and speed controls to media player
When listening to podcast, it is usual to want to speed up the playback.
https://github.com/miniflux/v2/pull/2521 was addressing the need globally, this PR
allow to address it for just the current open enclosure media. (no save) Some Browser
already include this control directly, but firefox does not (directly anyway).

Also, it is often useful to be able to skip chunk of a podcast, to skip commercials
for example, or get back a bit because we couldn't hear the last part. I added rudimentary
seek controls with the usual +/-10 and 30 seconds chuck size. This is pretty handy when podcast
are very long and using the seek bar is way too tricky to just skip 30s.

As always, I'm French and could only provide English and French translation for the few
text I added in the locale/translations files. Any help is welcome.

Tested mostly on Firefox (121.0) and quickly on Vivaldi(6.5.3206.53), chrome based.

Fixes: #1845 #1846
2024-04-26 13:44:26 -07:00
dependabot[bot] 9233568da3 Bump github.com/tdewolff/minify/v2 from 2.20.19 to 2.20.20
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.19 to 2.20.20.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.19...v2.20.20)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 19:09:18 -07:00
Frédéric Guillot fb075b60b5 reader/processor: minifier is breaking HTML entry content 2024-04-23 20:31:52 -07:00
Frédéric Guillot 2c4c845cd2 http/response: add brotli compression support 2024-04-19 12:16:49 -07:00
bo0tzz 2caabbe939 fix: Use `FORCE_REFRESH_INTERVAL` config for category refresh 2024-04-19 11:58:13 -07:00
Frédéric Guillot 771f9d2b5f reader/fetcher: add brotli content encoding support 2024-04-19 10:50:46 -07:00
Romain de Laage 647c66e70a ui: add tag entries page 2024-04-14 20:08:38 -07:00
jvoisin b205b5aad0 reader/processor: minimize the feed's entries html
Compress the html of feed entries before storing it. This should reduce the
size of the database a bit, but more importantly, reduce the amount of data
sent to clients

minify being [stupidly fast](https://github.com/tdewolff/minify/?tab=readme-ov-file#performance), the performance impact should be in the noise level.
2024-04-10 19:48:48 -07:00
goodfirm 4ab0d9422d chore: fix function name in comment
Signed-off-by: goodfirm <fanyishang@yeah.net>
2024-04-10 19:36:30 -07:00
Frédéric Guillot 38b80d96ea storage: change GetReadTime() function to use entries_feed_id_hash_key index 2024-04-09 20:37:30 -07:00
Michael Kuhn 35edd8ea92 Fix clicking unread counter
When clicking the unread counter, the following exception occurs:
```
Uncaught TypeError: Cannot read properties of null (reading 'getAttribute')
```

This is due to `onClickMainMenuListItem` not working correctly for the
unread counter `span`s, which return `null` when using `querySelector`.
2024-04-09 20:36:42 -07:00
Alexandros Kosiaris f0cb041885 Add back removed other repo owners in GH docker actions
In cf96ab45c1, support was added for using Docker related Github
actions in repositories of other owners. This was pretty helpful as it
allowed running modified forks off of main in a nightly fashion before
patches were pushed upstream. This was 6e870cdccc, add it
back
2024-04-06 11:31:29 -07:00
Frédéric Guillot fdd1b3f18e database: entry URLs can exceeds btree index size limit 2024-04-04 20:22:23 -07:00
Frédéric Guillot 6e870cdccc ci: use docker/metadata-action instead of deprecated shell-scripts 2024-04-04 18:04:32 -07:00
Michael Kuhn 194f517be8 Improve Dockerfiles
- Specify Docker registry explicitly (e.g., Podman does not use
  `docker.io` by default)
- Use `make miniflux` instead of duplicating `go build` arguments (this
  leverages Go's PIE build mode)
- Enable cgo to fix ARM containers (we need to make sure to use the same
  OS version for both container stages to avoid libc issues)
2024-04-04 17:36:28 -07:00
dependabot[bot] 11fd1c935e Bump golang.org/x/net from 0.23.0 to 0.24.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.24.0.
- [Commits](https://github.com/golang/net/compare/v0.23.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 16:37:41 -07:00
dependabot[bot] 47e1111908 Bump golang.org/x/term from 0.18.0 to 0.19.0
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/term/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 16:08:16 -07:00
dependabot[bot] c5b812eb7b Bump golang.org/x/oauth2 from 0.18.0 to 0.19.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 15:46:40 -07:00
dependabot[bot] 53be550e8a Bump github.com/yuin/goldmark from 1.7.0 to 1.7.1
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 17:30:47 -07:00
dependabot[bot] d0d693a6ef Bump golang.org/x/net from 0.22.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 17:30:26 -07:00
Evan Elias Young 1b8c45d162 finder: Find feed from YouTube playlist
The feed from a YouTube playlist page is derived in practically the same way as a feed from a YouTube channel page.
2024-04-01 21:16:32 -07:00
jvoisin 19ce519836 reader/rewrite: add a rule for oglaf.com
By default, Oglaf show some disclaimer/warning about its content, and this
doesn't play well with rss readers, so let's rewrite it to show the actual
comic instead of a placeholder.
2024-04-01 21:05:01 -07:00
Thomas J Faughnan Jr 3e0d5de7a3 api tests: use intSize-agnostic random integers
rand.Intn(math.MaxInt64) causes tests to fail on 32-bit architectures.
Use the simpler rand.Int() instead, which still provides plenty of room
for generating pseudo-random test usernames.
2024-04-01 21:02:48 -07:00
Frédéric Guillot 0336774e8c Update ChangeLog 2024-03-30 14:39:41 -07:00
Jean Khawand 756dd449cc
integration/webhook: add category title to request body 2024-03-29 16:37:05 -07:00
Taylan Tatlı a0b4665080 Turkish Translation Update 2024-03-28 19:09:24 -07:00
dependabot[bot] 6592c1ad6b Bump dominikh/staticcheck-action from 1.3.0 to 1.3.1
Bumps [dominikh/staticcheck-action](https://github.com/dominikh/staticcheck-action) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/dominikh/staticcheck-action/releases)
- [Changelog](https://github.com/dominikh/staticcheck-action/blob/master/CHANGES.md)
- [Commits](https://github.com/dominikh/staticcheck-action/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: dominikh/staticcheck-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 17:02:21 -07:00
jvoisin f109e3207c reader/rss: don't add empty tags to RSS items
This commit adds a bunch of checks to prevent reader/rss from adding empty tags
to rss items, as well as some minor refactors like nested conditions and loops
unrolling.
2024-03-24 19:46:56 -07:00
Romain de Laage b54fe66809 fix: do not store empty tags 2024-03-24 14:50:18 -07:00
jvoisin 93c9d43497 http/response: get rid of the X-XSS-Protection header
It's useless at best, dangerous at worst, and shouldn't be used anymore
anywhere. See the following resources for details:

- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
- https://chromestatus.com/feature/5021976655560704
- https://bugzilla.mozilla.org/show_bug.cgi?id=528661
- https://blogs.windows.com/windows-insider/2018/07/25/announcing-windows-10-insider-preview-build-17723-and-build-18204/
2024-03-24 13:45:38 -07:00
Frédéric Guillot e3b3c40c28 timezone: make sure the tests pass when the timezone database is not installed on the host 2024-03-24 13:25:02 -07:00
Frédéric Guillot 068790fc19 integration: fix rssbrige import 2024-03-24 12:42:29 -07:00
Frédéric Guillot 41d99c517f Update GitHub PR template 2024-03-23 14:34:03 -07:00
Frédéric Guillot 3db3f9884f cli: avoid misleading error message when creating an admin user 2024-03-23 14:32:55 -07:00
Frédéric Guillot ad1d349a0c rss: use Channel tags only if there is no Item tags 2024-03-23 13:46:48 -07:00
Jean Khawand 7ee4a731af Update miniflux.1 2024-03-21 19:59:02 -07:00
Jean Khawand 3c822a45ac Update miniflux.1
#2187  #2543
2024-03-21 19:59:02 -07:00
Frédéric Guillot c2311e316c Rename PROXY_* options to MEDIA_PROXY_* 2024-03-20 21:28:28 -07:00
jvoisin ed20771194 Enable trusted-types
This commit adds a policy, and make use of it in the Content-Security-Policy.

I've tested it the best I could, both on a modern browser supporting
trusted-types (Chrome) and on one that doesn't (firefox).

Thanks to @lweichselbaum for giving me a hand to wrap this up!
2024-03-20 17:50:37 -07:00
jvoisin beb8c80787 Replace a bunch of `let` with `const`
According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const

> Many style guides (including MDN's) recommend using const over let whenever a
variable is not reassigned in its scope. This makes the intent clear that a
variable's type (or value, in the case of a primitive) can never change.
2024-03-20 17:36:01 -07:00
jvoisin fc4bdf3ab0 Inline a one-liner function
No need to expose a symbol for this.
2024-03-20 17:21:30 -07:00
Frédéric Guillot 6bc819e198 man page: sort config options in alphabetical order 2024-03-19 22:22:24 -07:00
Frédéric Guillot 08640b27d5 Ensure enclosure URLs are always absolute 2024-03-19 21:57:46 -07:00
jvoisin 4be993e055 Minor refactoring of internal/reader/atom/atom_10_adapter.go
- Move the population of the feed's entries into a new function, to make
  `BuildFeed` easier to understand/separate concerns/implementation details
- Use `sort+compact` instead of `compact+sort` to remove duplicates
- Change `if !a { a = } if !a {a = }` constructs into `if !a { a = ; if !a {a = }}`.
  This reduce the number of comparisons, but also improves a tad the
  control-flow readability.
2024-03-19 20:41:44 -07:00
jvoisin 9df12177eb Minor idiomatic pass on internal/http/request/context.go 2024-03-19 20:21:23 -07:00
Jean Khawand a78d1c79da
Add `FILTER_ENTRY_MAX_AGE_DAYS` config option to limit fetching all feed items 2024-03-20 02:58:53 +00:00
Matt Behrens 1ea3953271
Add keyboard shortcuts for scrolling to top/bottom of the item list 2024-03-19 19:30:38 -07:00
dependabot[bot] fe8b7a907e Bump github.com/coreos/go-oidc/v3 from 3.9.0 to 3.10.0
Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/coreos/go-oidc/releases)
- [Commits](https://github.com/coreos/go-oidc/compare/v3.9.0...v3.10.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-oidc/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-19 19:09:08 -07:00
Frédéric Guillot a15cdb1655 Fix regression in AbsoluteProxifyURL()
Regression introduced in commit 66b8483791

PR #2499
2024-03-18 20:48:20 -07:00
Frédéric Guillot fa9697b972 Remove trailing space in SiteURL and FeedURL 2024-03-18 17:51:06 -07:00
jvoisin 8e28e41b02 Use struct embedding to reduce code duplication 2024-03-18 16:23:44 -07:00
jvoisin e2ee74428a Minor concatenation-related simplifications in internal/storage/
Use plain strings concatenation instead of
building an array and then joining it.
2024-03-18 16:20:55 -07:00
jvoisin 863a5b3648 Simplify removeDuplicates
Use a sort+compact construct instead of doing it by hand with a hashmap. The
time complexity is now O(nlogn+n) instead of O(n), and space complexity around
O(logn) instead of O(n+uniq(n)), but it shouldn't matter anyway, since
removeDuplicates is only called to deduplicate tags.
2024-03-18 16:13:32 -07:00
jvoisin 91f5522ce0 Minor simplification of internal/reader/media/media.go
- Simplify a switch-case by moving a common condition above it.
- Remove a superfluous error-check: `strconv.ParseInt` returns `0` when passed
  an empty string.
2024-03-18 16:09:32 -07:00
Frédéric Guillot 8212f16aa2 atom: avoid debug message when the date is empty 2024-03-17 15:29:50 -07:00
Frédéric Guillot b1e73fafdf Enable go-critic linter and fix various issues detected 2024-03-17 13:52:34 -07:00
Frédéric Guillot f6404290ba Replace Optional{Int,Int64,Float64} with a generic function OptionalNumber() 2024-03-17 12:25:55 -07:00
jvoisin c29ca0e313 Minor simplifications of the rewriter
- Online some one-line functions
- Transform a free-standing function into a method
- Massively simplify `removeClickbait`
- Use a proper constant instead of a magic number in `applyFuncOnTextContent`
2024-03-17 12:15:46 -07:00
jvoisin 02a074ed26 Compile block/keep regex only once per feed
No need to compile them once for matching on the url,
once per tag, once per title, once per author, … one time is enough.
It also simplify error handling, since while regexp compilation can fail,
matching can't.
2024-03-17 12:08:03 -07:00
Romain de Laage 00dabc1d3c feat: Media player: Conrol playback speed
fix  #1845
2024-03-17 11:53:30 -07:00
Frédéric Guillot b68ada396a Rewrite API integration tests without build tags 2024-03-16 21:29:07 -07:00
Frédéric Guillot e299e821a6 Update GitHub PR template 2024-03-15 20:59:17 -07:00
Frédéric Guillot 0f17dfc7d6 Fix regressions introduced by PR #2476
'Toast' messages are broken and v hotkey opens in the same tab

Commit d25c032171
2024-03-15 20:55:32 -07:00
Frédéric Guillot 7c80d6b86d Fix download button loading label 2024-03-15 20:40:14 -07:00
Frédéric Guillot f6f63b5282 Avoid warnings in ui package
Remove unused variables and improve JSON decoding in
saveEnclosureProgression()
2024-03-15 19:49:39 -07:00
Frédéric Guillot 309fdbb9fc Fix force refresh 2024-03-15 19:42:09 -07:00
Frédéric Guillot e2d862f2f6 Display an error message on edit feed page when the feed URL is not unique 2024-03-15 19:07:54 -07:00
Frédéric Guillot 4834e934f2 Remove some duplicated code in RSS parser 2024-03-15 18:40:06 -07:00
Frédéric Guillot dd4fb660c1 Refactor Atom parser to use an adapter 2024-03-15 17:27:16 -07:00
jvoisin 2ba893bc79 Bump the number of simultaneous workers
We're in 2024, I'm pretty sure we can afford to have 16 simultaneous open http
connections at the same time, instead of only 5.
2024-03-15 14:05:58 -07:00
Frédéric Guillot 7a307f8e74 Fix regression: Add to Home Screen button is unreadable
Regression introduced in commit ea58bac548
2024-03-14 17:37:50 -07:00
jvoisin 7310e13499 More trusted-types compatibility 2024-03-14 17:10:40 -07:00
dependabot[bot] bf6d286735 Bump github.com/go-webauthn/webauthn from 0.10.1 to 0.10.2
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.10.1 to 0.10.2.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.10.1...v0.10.2)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-14 17:03:54 -07:00
Frédéric Guillot ca919c2ff8 Fix JavaScript error on the login page 2024-03-13 21:47:23 -07:00
Frédéric Guillot 5948786b15 Add support for RSS <media:category> element 2024-03-13 21:35:39 -07:00
jvoisin f4746a7306 Fix and simplify shaarli's integration
- The jwt token was declared as using HS256 as algorithm, but was using HS512.
- No need to base64-encode then remove the padding when we can simply encode
  without padding.
- Factorize the header+payload concatenation as data

Odds are that this integration was broken from the start (HS512 vs HS256), so
I'm not sure if it's better to add tests or to simply get rid of it.
2024-03-13 21:34:57 -07:00
Frédéric Guillot 648b9a8f6f Refactor RSS Parser to use an adapter 2024-03-13 21:25:09 -07:00
jvoisin 66b8483791 Minor simplification of internal/proxy/proxy.go
- re-use ProxifiedUrl to implement AbsoluteProxifyURL, reducing the copy-pasta
- reduce the internal indentation of ProxifiedUrl by inverting some conditions
2024-03-13 19:42:01 -07:00
jvoisin e0ee28c013 More progress towards trusted-types
Create a new function `addIcon` and use it to add icons, instead of
operating on raw html.
2024-03-13 19:35:20 -07:00
dependabot[bot] d862d86f90 Bump google.golang.org/protobuf from 1.32.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 18:31:50 -07:00
jvoisin d25c032171 Simplify bootstrap.js
- Don't use lambdas to return a function, use directly the function instead.
- Remove a hack for "Chrome 67 and earlier" since it was released in 2018.
2024-03-13 18:26:27 -07:00
Frédéric Guillot 8429c6b0ab Refactor JSON Feed parser to use an adapter 2024-03-12 22:37:14 -07:00
Frédéric Guillot 6bc4b35e38 Refactor RDF parser to use an adapter
Avoid tight coupling between `model.Feed` and the original XML RDF feed.
2024-03-12 20:54:05 -07:00
mcnesium ee3486af66 align min-width with the other min-width values 2024-03-12 18:58:30 -07:00
jvoisin 45d486b919 When detecting the format, detect its version as well
There is no need to detect the format and then the version when both can be
done at the same time.

Add a benchmark as well, on large and small atom and rss files.
2024-03-12 18:56:56 -07:00
dependabot[bot] 688b73b7ae Bump github.com/tdewolff/minify/v2 from 2.20.18 to 2.20.19
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.18 to 2.20.19.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.18...v2.20.19)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-12 17:38:27 -07:00
Frédéric Guillot 6d97f8b458 Parse podcast categories 2024-03-11 22:30:27 -07:00
Frédéric Guillot f8e50947f2 Move iTunes and GooglePlay XML definitions to their own packages 2024-03-11 22:09:31 -07:00
Frédéric Guillot 9a637ce95e Refactor RSS parser to use default namespace
This change avoid some limitations of the Go XML parser regarding XML namespaces
2024-03-11 21:07:13 -07:00
Frédéric Guillot d3a85b049b jsminifier: set JavaScript version 2024-03-11 19:02:52 -07:00
jvoisin 5bcb37901c Use crypto.GenerateRandomBytes instead of doing it by hand
This makes the code a bit shorter, and properly handle
cryptographic error conditions.
2024-03-11 16:31:43 -07:00
jvoisin 9c8a7dfffe Make use of HashFromBytes everywhere
It feels a bit silly to have a function and to not make use of it.
2024-03-11 15:22:22 -07:00
jvoisin 74e4032ffc Small refactor of app.js
- replace a lot of `let` with `const`
- inline some `querySelectorAll` calls
- reduce the scope of some variables
- use some ternaries where it makes sense
- inline one-line functions
2024-03-11 15:18:57 -07:00
jvoisin fd1fee852c Simplify DomHelper.getVisibleElements
Use a `filter` instead of a loop with an index.
2024-03-11 15:03:00 -07:00
Frédéric Guillot c51a3270da GitHub Actions: Add basic ESLinter checks 2024-03-10 20:57:27 -07:00
Frédéric Guillot 45fa641d26 Fix JavaScript linter path in GitHub Actions 2024-03-10 20:37:18 -07:00
jvoisin fd8f25916b First steps towards trusted-types support
Refactor away some trival usages of `.innerHTML`. Unfortunately, there is no way to
enabled trusted-types in report-only mode via `<meta>` tags, see
https://github.com/w3c/webappsec-csp/issues/277
2024-03-10 20:14:30 -07:00
jvoisin 826e4d654f Replace DomHelper.findParent with .closest
See https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
2024-03-10 20:06:54 -07:00
jvoisin d9d17f0d69 Use a `Set` instead of an array in a KeyboardHandler's member
The variable `triggers` is only used to check if in contains a particular
value. Given that the number of keyboard shortcuts is starting to be
significant, let's future-proof the performances and use a `Set` instead of an
`Array` instead.
2024-03-10 19:41:13 -07:00
Frédéric Guillot eaaeb68474 Fix conditions to publish packages in GitHub workflows 2024-03-10 12:25:13 -07:00
Frédéric Guillot 382885f144 Update changeLog 2024-03-10 10:50:47 -07:00
dependabot[bot] 0f7b047b0a Bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.1 to 3.0.3.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3.0.3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.1...v3.0.3)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 20:59:42 -08:00
jvoisin a074773e6c Use an io.ReadSeeker instead of an io.Reader to parse feeds
This will allow to make use of func (*Reader) Seek, instead of re-recreating a
new reader. It's a large commit for a small change, but anything to simply the
reader/buffer/ReadAll/… mess is a step in the right direction I think, and it
should enable more follow-up simplifications.
2024-03-06 20:13:39 -08:00
jvoisin 3d0126be0b Speed the sanitizer up a bit, again
- allow youtube urls to start with `www`
- use `strings.Builder` instead of a `bytes.Buffer`
- use a `strings.NewReader` instead of a `bytes.NewBufferString`
- sprinkles a couple of `continue` to make the code-flow more obvious
- inline calls to `inList`, and put their parameters in the right order
- simplify isPixelTracker
- simplify `isValidIframeSource`, by extracting the hostname and comparing it
  directly, instead of using the full url and checking if it starts with
  multiple variations of the same one (`//`, `http:`, `https://` multiplied by
  ``/`www.`)
- add a benchmark
2024-03-05 19:31:50 -08:00
dependabot[bot] eda2e2f3f5 Bump golang.org/x/oauth2 from 0.17.0 to 0.18.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-05 15:39:07 -08:00
jvoisin 111e3f2106 Reuse a Reader instead of copying to a buffer when parsing an atom feed 2024-03-04 17:36:10 -08:00
dependabot[bot] c1ec77a42c Bump golang.org/x/net from 0.21.0 to 0.22.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to 0.22.0.
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 16:48:02 -08:00
jvoisin 3339d9d3d7 Preallocate memory when exporting to OPML
This should marginally increase performance when export a large amount of feeds
to OPML.
2024-03-03 20:34:37 -08:00
jvoisin 8d80e9103f Delay call of `view.New` after logging the user in
There is no need to do extra work like creating a session and its associated
view until the user has been properly identified and as many possibly-failing sql request have been successfully run.
2024-03-03 20:32:15 -08:00
jvoisin d55b410800 Use constant-time comparison for anti-csrf tokens
This is probably completely overkill, but since anti-csrf tokens are secrets,
they should be compared against untrusted inputs in constant time.
2024-03-03 20:28:13 -08:00
jvoisin 9fe99ce7fa Simplify and optimize genericProxyRewriter
- Reduce the amount of nested loops: it's preferable to search the whole page
  once and filter on it (even with filters that should always be false),
  than searching it again for every element we're looking for.
- Factorize the proxying conditions into a `shouldProxy` function to reduce the
  copy-pasta.
2024-03-03 20:25:47 -08:00
Thiago Perrotta b8df6c31a0 sort integrations alphabetically 2024-03-03 20:19:42 -08:00
Frédéric Guillot abdd5876a1 Move search form to a dedicated page 2024-03-01 16:56:15 -08:00
Frédéric Guillot 1b5edfc00a Add unit test to ensure each translation has the correct number of plurals 2024-02-29 20:44:08 -08:00
jvoisin 347740dce1 Speed up removeUnlikelyCandidates
`.Not` returns a brand new Selection, copied element by element.
2024-02-29 19:38:43 -08:00
jvoisin ab85d4d678 Improve EstimateReadingTime's speed by a factor 7
- Refactorise the tests and add some
- Use 250 signs instead of the whole text
- Only check for Korean, Chinese and Japanese script
- Add a benchmark
- Use a more idiomatic control flow

```console
$ # main branch
$ go test -bench=.
goos: linux
goarch: amd64
pkg: miniflux.app/v2/internal/reader/readingtime
BenchmarkEstimateReadingTime-12              267           4821268 ns/op
PASS
ok      miniflux.app/v2/internal/reader/readingtime     1.754s
$ # speed_up_reading_time branch
$ go test -bench=.
goos: linux
goarch: amd64
pkg: miniflux.app/v2/internal/reader/readingtime
cpu: 12th Gen Intel(R) Core(TM) i7-1265U
BenchmarkEstimateReadingTime-12             1941            653312 ns/op
PASS
ok      miniflux.app/v2/internal/reader/readingtime     1.342s
$
```
2024-02-29 19:24:15 -08:00
jvoisin 31ac62f410 Don't compute reading-time when unused
If the user doesn't display reading times, there is no need to compute them.
This should speed things up a bit, since `whatlanggo.Detect` is abysmally slow.
2024-02-29 19:14:17 -08:00
Frédéric Guillot 97765b93a9 Revert "Minor internal/reader/readability/readability.go speedup"
This reverts commit 4db138d4b8.

```
panic: runtime error: index out of range [-1]

goroutine 49 [running]:
miniflux.app/v2/internal/reader/readability.getArticle.func1(0x8?, 0xc000b56570)
        /home/fred/repos/miniflux/v2/internal/reader/readability/readability.go:120 +0x2ac
github.com/PuerkitoBio/goquery.(*Selection).Each(0xc000b56510, 0xc000892fa8)
        /home/fred/go/pkg/mod/github.com/!puerkito!bio/goquery@v1.9.0/iteration.go:10 +0x62
miniflux.app/v2/internal/reader/readability.getArticle(0xc00044f1f0, 0xc000a04a50)
        /home/fred/repos/miniflux/v2/internal/reader/readability/readability.go:101 +0x15d
miniflux.app/v2/internal/reader/readability.ExtractContent({0x1005d00?, 0xc0001522d0?})
        /home/fred/repos/miniflux/v2/internal/reader/readability/readability.go:91 +0x211
miniflux.app/v2/internal/reader/scraper.ScrapeWebsite(0xc000893688?, {0xc0007ce720, 0x54}, {0x0, 0x0})
        /home/fred/repos/miniflux/v2/internal/reader/scraper/scraper.go:63 +0x859
miniflux.app/v2/internal/reader/processor.ProcessFeedEntries(0xc000133188, 0xc000502c40, 0xc0003e6360, 0x0)
        /home/fred/repos/miniflux/v2/internal/reader/processor/processor.go:77 +0x8ea
miniflux.app/v2/internal/reader/handler.RefreshFeed(0xc000133188, 0x10cf, 0x52d5c, 0x0)
        /home/fred/repos/miniflux/v2/internal/reader/handler/handler.go:301 +0x1485
miniflux.app/v2/internal/cli.refreshFeeds.func1(0x0)
        /home/fred/repos/miniflux/v2/internal/cli/refresh_feeds.go:59 +0x2d7
created by miniflux.app/v2/internal/cli.refreshFeeds in goroutine 1
        /home/fred/repos/miniflux/v2/internal/cli/refresh_feeds.go:50 +0x5d5
```
2024-02-29 19:06:03 -08:00
dependabot[bot] f858ad5f26 Bump github.com/PuerkitoBio/goquery from 1.9.0 to 1.9.1
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-29 18:36:57 -08:00
jvoisin e6524f925f Simplify username generation for the tests
No need to generate random numbers 10 times, generate a single big-enough one.
A single int64 should be more than enough
2024-02-29 18:36:34 -08:00
Frédéric Guillot c493f8921e Add missing regex anchor detected by CodeQL 2024-02-28 20:50:17 -08:00
Frédéric Guillot b2ce98da87 Add missing plurals for some languages 2024-02-28 20:38:10 -08:00
jvoisin 4db138d4b8 Minor internal/reader/readability/readability.go speedup
- Don't use a capturing group in `divToPElementsRegexp`
- Remove a duplicate condition
- Replace a regex with a fixed-comparison and a `Contains`
2024-02-28 20:03:14 -08:00
jvoisin f12d5131b0 Divide the sanitization time by 3
Instead of having to allocate a ~100 keys map containing possibly dynamic
values (at least to the go compiler), allocate it once in a global variable.
This significantly speeds things up, by reducing the garbage
collector/allocator involvements.

Local synthetic benchmarks have shown a improvements from 38% of wall time to only
12%.
2024-02-28 20:00:13 -08:00
jvoisin 1f5c8ce353 Don't mix up capacity and length
- `make([]a, b)` create a slice of `b` elements `a`
- `make([]a, b, c)` create a slice of `0` elements `a`, but reserve space for `c` of them

When using `append` on the former, it will result on a slice with `b` leading
elements, which is unlikely to be what we want. This commit replaces the two
instances where this happens with the latter construct.
2024-02-28 19:57:30 -08:00
jvoisin 645a817685 Use modern for loops
Go 1.22 introduced a new [for-range](https://go.dev/ref/spec#For_range)
construct that looks a tad better than the usual `for i := 0; i < N; i++`
construct. I also tool the liberty of replacing some
`for i := 0; i < len(myitemsarray); i++ { … myitemsarray[i] …}`
with  `for item := range myitemsarray` when `myitemsarray` contains only pointers.
2024-02-28 19:55:28 -08:00
jvoisin f4f8342245 Remove a superfluous condition
No need to check if the length of `line` is positive since we're checking
afterwards that it contains the `=` sign.
2024-02-28 19:47:30 -08:00
jvoisin 543a690bfd Close resources as soon as possible, instead of using defer() in a loop
So that resources can be freed as soon as they're not used anymore, instead of
waiting for the two nested loops to finish.
2024-02-28 19:47:30 -08:00
jvoisin c4e5dad549 Remove superfluous escaping in a regex 2024-02-28 19:47:30 -08:00
jvoisin fa12c23d79 Use strings.ReplaceAll instead of strings.Replace(…, -1) 2024-02-28 19:47:30 -08:00
jvoisin 4fe902a5d2 Use `strings.EqualFold` instead of `strings.ToLower(…) ==` 2024-02-28 19:47:30 -08:00
jvoisin 61af08a721 Use .WriteString( instead of .Write([]byte(… 2024-02-28 19:47:30 -08:00
jvoisin b04550e2f2 Use `%q` instead of `"%s"` 2024-02-28 19:47:30 -08:00
jvoisin 5e5cb056c5 Make internal/worker/worker.go read-only
Since workers don't communicate anything back to the pool with the channel,
there is no need to have it bidirectional.
2024-02-28 19:39:03 -08:00
jvoisin 48fa64f8ec Use a switch-case construct in internal/locale/plural.go instead of an avalanche of if-if-if-if-if
Less lines or code and marginally greater readability, yay!
Oh and also preallocate a map in LoadCatalogMessages just because we can.
2024-02-28 19:36:38 -08:00
jvoisin f274394f0e Simplify formatFileSize
No need to use a loop with divisions and multiplications when we have logarithms.
2024-02-28 19:32:38 -08:00
jvoisin 9a4a942cc4 Simplify durationImpl 2024-02-28 19:32:38 -08:00
jvoisin 6b3b8e8c9b Inline some templating functions 2024-02-28 19:32:38 -08:00
jvoisin 5a7d6f8997 Make use of printer.Print when possible 2024-02-28 19:24:41 -08:00
jvoisin b4ed17fbac Add a printer.Print to internal/locale/printer.go
No need to use variadic functions with string format interpolation
to generate static strings.
2024-02-28 19:24:41 -08:00
dependabot[bot] 57476f4d59 Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.19.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 21:25:42 -08:00
jvoisin 7660910232 Use prepared statement for intervals 2024-02-27 21:25:25 -08:00
jvoisin b054506e3a Use proper prepared statements for ArchiveEntries 2024-02-27 21:25:25 -08:00
jvoisin c961c6db7d Use proper prepared statement for updateEnclosures 2024-02-27 21:25:25 -08:00
Frédéric Guillot 0f126d4d11 Fix CodeQL workflow 2024-02-27 21:01:38 -08:00
jvoisin b94756bbf0 Add a warning for StripTags 2024-02-27 20:41:47 -08:00
jvoisin db6ae707ef Add some tests for add_image_title
I'm not sure if the behaviour is expected, but I didn't manage to
get the content injection to work in my browser, so I guess it's alright?
2024-02-27 20:41:15 -08:00
Frédéric Guillot 97feec8ebf Add more URL validation in media proxy 2024-02-26 20:29:40 -08:00
jvoisin bce21a9f91 Remove github.com/google/uuid
Replace it with a hand-rolled implementation. Heck, an UUID isn't even a
requirement, according to [omnivore](https://docs.omnivore.app/integrations/api.html#saving-a-url-with-the-api)'s
documentation, any "unique id" would do.
2024-02-26 18:31:12 -08:00
jvoisin 06e256e5ef Simplify internal/reader/icon/finder.go
- Use a simple regex to parse data uri instead of a hand-rolled parser, and
  document what fields are considered mandatory.
- Use case-insensitive matching to find (fav)icons, instead of doing the same
  query twice with different letter cases
- Add 'apple-touch-icon-precomposed.png' as a fallback favicon
- Reorder the queries to have i`con` first, since it seems to be the most
  popular one. It used to be last, meaning that pages had to be parsed
  completely 4 times, instead of one now.
- Minor factorisation in findIconURLsFromHTMLDocument
2024-02-26 18:18:04 -08:00
jvoisin 040938ff6d Small refactoring of internal/reader/date/parser.go
- Split dates formats into those that require local times
  and those who don't, so that there is no need to have a switch-case in the
  for loop with around 250 iterations at most.
- Be more strict when it comes to timezones, previously invalid ones like -13
  were accepted. Also add a test for this.
- Bail out early if the date is an empty string.
2024-02-26 18:08:04 -08:00
dependabot[bot] 21da7f77f5 Bump golang.org/x/crypto from 0.19.0 to 0.20.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 18:01:00 -08:00
jvoisin c2d2f31438 Improve a bit internal/reader/scraper/scraper.go
- make findContentUsingCustomRules' more idiomatic,
  since in golang a function returning an error might
  return garbage in other parameter. Moreover, ignoring
  errors is bad practise.
- getPredefinedScraperRules is now running in constant-time,
  instead of iterating on a list with around 50 items in it.
2024-02-26 18:00:23 -08:00
jvoisin 5b2558bf92 Miscellaneous improvements to internal/reader/subscription/finder.go
- Surface `localizedError` in FindSubscriptionsFromWellKnownURLs via slog
- Use an inline declaration for new subscriptions, like done elsewhere in the
  file, if only for consistency's sake
- Preallocate the `subscriptions` slice when using an RSS-bridge,
  it's a good practise, and it might even marginally improve
  performances when adding __a lot__ of feeds via an rss-bridge instance, wooo!
2024-02-26 17:52:21 -08:00
jvoisin ecd59009fb Add a couple of new possible locations for feeds
- Hugo likes to generate index.xml
- feed.atom and feed.rss are used by enterprise-scale/old-school gigantic CMS
2024-02-26 17:43:51 -08:00
jvoisin 4a943b722d Add a couple of fuzzers 2024-02-26 17:23:49 -08:00
Frédéric Guillot 9d1b1e19d4 Google Reader: Do not return a 500 error when no items is returned 2024-02-25 21:17:49 -08:00
Frédéric Guillot 7a8061fc72 Fix regression introduced in PR #2402 2024-02-25 20:45:34 -08:00
jvoisin bca84bac8b Use an update-where for MarkCategoryAsRead instead of a subquery 2024-02-25 17:50:30 -08:00
jvoisin 66e0eb1bd6 Reformat's ArchiveEntries's query for consistency's sake
And replace the `=ANY` with an `IN`
2024-02-25 17:50:30 -08:00
jvoisin 26d189917e Simplify cleanupEntries' query
- `NOT (hash=ANY(%4))` can be expressed as `hash NOT IN $4`
- There is no need for a subquery operating on the same table,
  moving the conditions out is equivalent.
2024-02-25 17:50:30 -08:00
jvoisin ccd3955bf4 Format GetReadTime's query for consistency's sake 2024-02-25 17:50:30 -08:00
jvoisin 8a2cc3a344 Reformat the query in GetEntryIDs
To make it more consistent with how all the other are formatted
2024-02-25 17:50:30 -08:00
jvoisin 647fa025f8 Simplify WeeklyFeedEntryCount
No need for a `BETWEEN`: we want to filter on entries published in the last
week, no need to express is as "entries published between now and last week",
"entries published after last week" is enough.
2024-02-25 17:50:30 -08:00
jvoisin 1955350318 Build the map inline in CountAllFeeds()
No need to build an empty map to then add more fields in it one by one.
2024-02-25 17:50:30 -08:00
jvoisin 04916a57d2 Simplify CleanOldUserSessions' query
No need for a subquery, filtering on `created_at` directly is enough.
2024-02-25 17:50:30 -08:00
jvoisin 0adac5c6f7 Minor code simplification in internal/ui/view/view.go
No need to create the map item by item when we
can create it in one go.
2024-02-25 17:31:44 -08:00
jvoisin 54b5be5e7d Significantly simplify/speed up the sanitizer
- Use constant time access for maps instead of iterating on them
- Build a ~large whitelist map inline instead of constructing it item by item
  (and remove a duplicate key/value pair)
- Use `slices` instead of hand-rolled loops
2024-02-25 17:29:46 -08:00
Frédéric Guillot eae4cb1417 Add feed option to disable HTTP/2 to avoid fingerprinting 2024-02-24 22:30:26 -08:00
Frédéric Guillot 420a3d4d95 Remove Golint
- Golint is deprecated
- Use staticcheck and golangci-lint instead
2024-02-24 21:17:56 -08:00
jvoisin b48ad6dbfb Make use of go≥1.21 slices package instead of hand-rolled loops
This makes the code a tad smaller, moderner,
and maybe even marginally faster, yay!
2024-02-24 20:22:53 -08:00
jvoisin 2be5051b19 Reorder the fields of the Entry struct to save some memory
Given that there is always a ton of `Entry` floating around, reordering its
field to take less space is a quick/simple way to reduce miniflux' memory
consumption.

I kept the `ID` field as the first member, as I think it's the most important
one, and moving it somewhere else would drown it in other fields.

Anyway, this still provides a reduction of 32 bytes per Entry:

```console
$ fieldalignment  ./client/model.go 2>&1 | grep 203
~/v2/client/model.go:203:12: struct with 280 pointer bytes could be 240
$ fieldalignment  ./client/model.go 2>&1 | grep 203
~/v2/client/model.go:203:12: struct with 248 pointer bytes could be 240
$
```

The same optimisation pass could be applied to other structs, but since they
aren't present in obviously great numbers during miniflux' life cycle, it would
likely require some profiling to see if it's worth doing it.
2024-02-24 20:08:27 -08:00
jvoisin c544dadd55 Fix categories import from Thunderbird's OPML
Thunderbird OPML exports are looking like this:

```xml
<opml version="1.0" xmlns:fz="urn:forumzilla:">
<head>
	<title>Thunderbird OPML Export - RSS</title>
    	<dateCreated>Sat, 24 Feb 2024 11:31:13 GMT</dateCreated>
</head>
<body>
	<outline title="News">
		<outline type="rss" ...>
		<outline type="rss" ...>
		...
	</outline>
	<outline title="Blogs">
		<outline type="rss" ...>
		<outline type="rss" ...>
		...
	</outline>
</body>
```

This commit make it so that categories are now correctly imported.
2024-02-24 19:43:33 -08:00
Frédéric Guillot 1da65d97d8 Proxify video poster attribute 2024-02-23 18:44:20 -08:00
Frédéric Guillot c595c80356 Handle RDF feeds with duplicated <title> elements 2024-02-23 17:40:58 -08:00
dependabot[bot] 20e5fbcd7a Bump github.com/PuerkitoBio/goquery from 1.8.1 to 1.9.0
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-23 16:32:21 -08:00
dependabot[bot] ac77154907 Bump github.com/tdewolff/minify/v2 from 2.20.17 to 2.20.18
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.17 to 2.20.18.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.17...v2.20.18)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-23 16:32:01 -08:00
Thomas J Faughnan Jr 97ace53bc9 Makefile: quiet git describe and rev-parse stderr 2024-02-21 22:08:02 -08:00
Frédéric Guillot feb962f98a Build amd64/arm64 Debian packages with CGO disabled
That should ensure that the binary is compiled statically
2024-02-21 21:23:48 -08:00
Frédéric Guillot 8602089a1e Adjust GitHub Actions condition for manual pipeline execution 2024-02-21 21:19:07 -08:00
Frédéric Guillot 4b0648f3d7 Update go.mod and add .exe suffix to Windows binary 2024-02-21 21:16:43 -08:00
Frédéric Guillot 856b96cbf8 Add job to build packages on-demand 2024-02-21 21:11:00 -08:00
Robert Lützner facf38955c Add 'Enter' key as a hotkey to open selected item
There are a few things that need to be done, to make this work.

First, we need to register `Enter` as another hotkey that opens the
selected item.

However, by default the `KeyboardHandler` will override all default
actions. That might make sense for any other key, but for the `Enter`
key, we want to keep the default behavior (i.e. follow a selected link
or press a button). So for this single key event, we do not call
`preventDefault()`.

I see this as unproblematic for the following reasons.

1. With the changes from #2348, when we're in a list of items (articles,
   categories, feeds), there is no link selected. This is what made the
   `Enter` key work _implicitly_ in the past. With nothing selected, the
   `Enter` key will do nothing by default.
2. If we have **any** link selected (including when we are in a view
   with a list of selectable items), we'll get the default action of
   `Enter` (i.e. follow a link), which is exactly what we had before.

Lastly, we need to update the list of keyboard shortcuts displayed when
pressing `?`.

This fixes #2366.
2024-02-21 20:02:58 -08:00
MSTCL cfdb890eae
Add Readeck integration 2024-02-21 19:57:34 -08:00
Thomas J Faughnan Jr 2f8d3a7958 Makefile: do not force CGO_ENABLED=0 for miniflux target 2024-02-21 19:47:58 -08:00
Frédéric Guillot 59311deb57 Fix logo misalignment when using languages that are more verbose than English 2024-02-19 15:10:35 -08:00
dependabot[bot] d2541a173a Bump github.com/tdewolff/minify/v2 from 2.20.16 to 2.20.17
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.16 to 2.20.17.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.16...v2.20.17)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 14:58:10 -08:00
Frédéric Guillot b618c11b80 Fix typo in man page and Changelog 2024-02-17 13:28:17 -08:00
Frédéric Guillot 8b4675807a Update ChangeLog 2024-02-17 12:07:36 -08:00
Frédéric Guillot c0bca973d6 Update GitHub Actions to Go 1.12 2024-02-17 12:00:23 -08:00
krvpb024 5c97771e61 fix macOS VoiceOver didn't announce details and summary expand 2024-02-14 20:11:23 -08:00
dependabot[bot] c9cbe8afd5 Bump golangci/golangci-lint-action from 3 to 4
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3 to 4.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 19:30:13 -08:00
knrdl 1d90ce9dd2
Add Linkwarden integration 2024-02-11 17:12:37 -08:00
knrdl ccb9eed573 fix wrong label on save
when saving an entry the label was reset on complete
so the desired done label was never shown
2024-02-11 12:49:08 -08:00
krvpb024 2221fd408d fix the page-button hover style not show 2024-02-09 19:37:10 -08:00
Tân Î-sîn ea58bac548
Replace link has button role with button tag
# Change HTML tag to button

Replace the link tag with an HTML button to prevent some screen readers from having confusing announcements. By using the HTML button, users can use the Enter and Space keys to activate actions by default, instead of implementing them in JavaScript.

# Differentiate links and buttons visually

When activating the link element, the user may expect the web page to navigate to the URL and the page will refresh; when activating the button element, the user may expect the web page to still be on the same page, so that their current state, such as: input value, won't disappear.

Links and buttons should have different styles visually, so that users can't expect what will happen when they activate a link or a button.

I added the underline to the links, because that is the common pattern. Buttons have border and background color in a common pattern. But I think that will change the current layout drastically. So I added the focus, hover and active classes to the buttons instead.
2024-02-09 17:09:30 -08:00
krvpb024 0f85c0511a remove item focus outline overlapped on current style 2024-02-09 16:54:29 -08:00
krvpb024 27749a2877 change focus target on items when using keyboard navigation 2024-02-09 16:54:29 -08:00
dependabot[bot] 0991c27f9d Bump golang.org/x/oauth2 from 0.16.0 to 0.17.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 19:56:08 -08:00
dependabot[bot] 00eab03655 Bump golang.org/x/net from 0.20.0 to 0.21.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 19:26:55 -08:00
dependabot[bot] e55377b204 Bump github.com/go-webauthn/webauthn from 0.10.0 to 0.10.1
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.10.0...v0.10.1)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 19:04:41 -08:00
dependabot[bot] 4ddc4ec002 Bump golang.org/x/crypto from 0.18.0 to 0.19.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/crypto/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 18:52:52 -08:00
krvpb024 facf17db3f remove icon img alt text 2024-02-07 21:59:09 -08:00
dependabot[bot] 8663c7d031 Bump golang.org/x/term from 0.16.0 to 0.17.0
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/term/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 21:58:05 -08:00
krvpb024 6eac968083 add keyboard shortcut and aria attribute to menu button 2024-02-07 21:56:24 -08:00
Frédéric Guillot bd573957e0 Debian packages are failing to build
Error seen on GitHub Actions:

```
-buildmode=pie requires external (cgo) linking, but cgo is not enabled
```
2024-02-07 21:35:06 -08:00
Frédéric Guillot 5ce5c47499 Remove translation key page.categories.unread_counter 2024-02-05 21:39:02 -08:00
Frédéric Guillot 9336891e67 Restore menu toggle when clicking on the logo
The caret icon is too small on smartphone to expand/collapse the menu
2024-02-05 21:18:06 -08:00
Frédéric Guillot aa30c35e7e Use numeric UID in Alpine Docker image
Same as PR #2332
2024-02-05 20:49:25 -08:00
krvpb024 39368ece9a add alert role to alert message element 2024-02-05 20:14:23 -08:00
krvpb024 4f57309380 remove button role on element which perform navigation 2024-02-05 20:10:38 -08:00
krvpb024 57e7bd5bc9 add button role to links with action 2024-02-05 20:10:38 -08:00
krvpb024 bf54222be7 hide menu button in desktop layout instead of icon 2024-02-04 21:36:31 -08:00
Sheogorath 552fb3e4cc Fix non-numeric UID
This patch adjusts the distroless image to use the predefined non-root UID, which uses explicit UID definitions. This allows orchestrators like Kubernetes to validate non-zero UIDs directly by checking the Image metadata.

The previous setup without an explicit `runAsUser` in the securityContext would produce the following error when enabling `runAsNonRoot`:

```
Error: container has runAsNonRoot and image has non-numeric user (nonroot), cannot verify user is non-root (pod: "miniflux-97cc5955f-pt7vf_miniflux(d1c56d29-ea0a-407c-b3f3-9821fbd7ee61)", container: miniflux)
```
2024-02-04 21:32:42 -08:00
Frédéric Guillot 7d9f174b3f Add missing label ID for custom CSS field 2024-02-04 13:41:23 -08:00
Frédéric Guillot bf4d31eebe Add styling to search button 2024-02-04 13:36:31 -08:00
Frédéric Guillot f203326a29 Improve translation of hidden aria elements 2024-02-04 13:12:54 -08:00
krvpb024 8367413e84 change links that could perform actions to buttons 2024-02-04 10:47:30 -08:00
krvpb024 9b6dbd422c change article html structure for accessibility 2024-02-04 10:47:30 -08:00
krvpb024 531e80f580 fix entry page layout has changed 2024-02-04 10:47:30 -08:00
krvpb024 890a34e1bd remove code for debug and comment 2024-02-04 10:47:30 -08:00
krvpb024 7413e383a8 fix search and star function 2024-02-04 10:47:30 -08:00
krvpb024 7496479380 change header tag usage to match landmark meaning 2024-02-04 10:47:30 -08:00
krvpb024 6c78a1d635 improve feed, entry, category a11y 2024-02-04 10:47:30 -08:00
krvpb024 6413c9f9f7 add nav landmark to settings and feed menu 2024-02-04 10:47:30 -08:00
krvpb024 352aeb0490 fix missing translation key 2024-02-04 10:47:30 -08:00
krvpb024 61f52d971a fix h1 font-size 2024-02-04 10:47:30 -08:00
krvpb024 fa7508e28d change search summary icon 2024-02-04 10:47:30 -08:00
krvpb024 c217a31444 fix search label and login view not define header 2024-02-04 10:47:30 -08:00
krvpb024 84576f2c29 fix menu responsive layout 2024-02-04 10:47:30 -08:00
krvpb024 da11416b39 change layout structure by moving header 2024-02-04 10:47:30 -08:00
krvpb024 6a9a590c7f add search landmark and disclosure pattern to menu 2024-02-04 10:47:30 -08:00
krvpb024 f23e6a3352 add skip to content link 2024-02-04 10:47:30 -08:00
krvpb024 b568b1d41d improve page-header a11y
add nav landmark for links
labeling the purpose of nav in page-header
labeling the meaning of total number in page-header title
2024-02-04 10:47:30 -08:00
dependabot[bot] 9980634e5d Bump github.com/yuin/goldmark from 1.6.0 to 1.7.0
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-02 17:37:49 -08:00
Matt Stobo 4a50ca9122 Allow filtering feeds on entry.Author 2024-01-31 19:42:07 -08:00
dependabot[bot] 3be0d14d44 Bump github.com/tdewolff/minify/v2 from 2.20.15 to 2.20.16
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.15 to 2.20.16.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.15...v2.20.16)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-29 18:57:28 -08:00
dependabot[bot] ec9fd996b1 Bump github.com/tdewolff/minify/v2 from 2.20.14 to 2.20.15
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.14 to 2.20.15.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.14...v2.20.15)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-26 18:44:21 -08:00
MDeLuise 1e704468a5 feat: add linkace service integration 2024-01-25 18:04:14 -08:00
Frédéric Guillot e8147f26b9 Fix incorrect label `for` attribute 2024-01-24 20:37:12 -08:00
Andrew Gunnerson 6648e0af38 Revert "touch_handler: Fix scroll up behavior on Firefox Android"
This reverts commit 344a237af8.

The previous behavior is more correct due to the use of preventDefault()
and the commit was introduced only as a workaround. As of [1], the
underlying issue in Firefox has been fixed and downward swipes to scroll
up are no longer ignored every other attempt.

[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1847305
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1853075
[3] https://bugzilla.mozilla.org/show_bug.cgi?id=1724755

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-01-23 19:33:08 -08:00
dependabot[bot] fde84d55ba Bump github.com/google/uuid from 1.5.0 to 1.6.0
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-23 19:32:19 -08:00
Dave 1159dd6982 Add `addDynamicIframe` rewrite function.
Add unit tests for `add_dynamic_iframe` rewrite.
2024-01-23 19:23:57 -08:00
Frédéric Guillot 50341759b6 Fix typo in log message 2024-01-22 20:15:38 -08:00
dzaikos d68f2306c6 Add attribute to add_dynamic_image rewrite candidates. 2024-01-21 14:27:06 -08:00
Christoffer Strömblad 578743de1f
Add `item-meta-info-reading-time` CSS class 2024-01-20 10:53:02 -08:00
Frédéric Guillot 8553188ae4 Add missing translation argument 2024-01-20 10:48:27 -08:00
Frédéric Guillot a3e2570df2
Update issue templates 2024-01-15 10:31:38 -08:00
Frédéric Guillot 87c9ef6b48 Rewrite relative RSS Bridge URL 2024-01-13 14:54:36 -08:00
Frédéric Guillot ce32d181d5 Change default Accept header 2024-01-13 13:53:57 -08:00
dependabot[bot] b8c6c64e9c Bump github.com/tdewolff/minify/v2 from 2.20.13 to 2.20.14
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.13 to 2.20.14.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.13...v2.20.14)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-12 15:11:41 -08:00
dependabot[bot] c51f092bda Bump github.com/tdewolff/minify/v2 from 2.20.12 to 2.20.13
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.12 to 2.20.13.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.12...v2.20.13)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 19:05:25 -08:00
Frédéric Guillot e2d33f680e Fix incorrect condition 2024-01-11 19:04:50 -08:00
Ryan Stafford 980c5c63df
Limit feed/category entry pagination to unread entries when coming from unread entry list 2024-01-09 21:44:25 -08:00
Filipe de Luna 1441dc7600
Update entry processor to allow blocking/keeping entries by tags 2024-01-09 21:15:11 -08:00
dependabot[bot] 6fc4e2f45e Bump golang.org/x/oauth2 from 0.15.0 to 0.16.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 17:11:00 -08:00
dependabot[bot] 8c00dbcf38 Bump github.com/tdewolff/minify/v2 from 2.20.10 to 2.20.12
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.10 to 2.20.12.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.10...v2.20.12)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 17:10:33 -08:00
dependabot[bot] 803e160c70 Bump golang.org/x/term from 0.15.0 to 0.16.0
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/term/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-04 19:16:28 -08:00
notsmarthuman 4590da2fc3
Add `FORCE_REFRESH_INTERVAL` config option 2024-01-02 18:33:15 -08:00
Dark Dragon a1879ea37c Create default miniflux db 2023-12-31 10:54:53 -08:00
dependabot[bot] 8fe289ca72 Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-31 10:02:23 -08:00
Stephan Brauer eb9ac861ea Update German translation.
- Translate missing entries.
- Hiphenate some phrases.
- Improve some translation.
  - Some translations where seemingly done automatically.
  - Some translation could be phrased a bit better (subjectively).
2023-12-31 10:01:48 -08:00
Jan Tojnar 074393d3bf fix: Include type for OPML subscriptions
As per [OPML 2.0 specification]:

> Each sub-element of the body of the OPML document is a node of type rss or an outline element that contains nodes of type rss.

> Required attributes: type, text, xmlUrl.

[OPML 2.0 specification]: http://opml.org/spec2.opml#subscriptionLists
2023-12-31 10:00:50 -08:00
dependabot[bot] 538e5305d3 Bump github.com/go-webauthn/webauthn from 0.9.4 to 0.10.0
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.9.4 to 0.10.0.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.9.4...v0.10.0)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-30 20:59:03 -08:00
dependabot[bot] 917852bbb0 Bump golang.org/x/crypto from 0.16.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-30 20:56:13 -08:00
dependabot[bot] c4e0dc3f5e Bump github.com/tdewolff/minify/v2 from 2.20.9 to 2.20.10
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.9 to 2.20.10.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.9...v2.20.10)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-30 20:55:55 -08:00
dependabot[bot] 22ed3a3565 Bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-30 20:55:41 -08:00
dependabot[bot] 80853d48f5 Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-30 20:55:20 -08:00
Darwin d90667777f request_builder.go: fetcher: Force try HTTP/2 2023-12-15 16:27:00 -08:00
Frédéric Guillot 7990edd345 Update ChangeLog 2023-12-13 17:59:46 -08:00
dependabot[bot] 648804e5dc Bump github.com/google/uuid from 1.4.0 to 1.5.0
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-12 20:25:28 -08:00
dependabot[bot] 187e75de4d Bump github.com/coreos/go-oidc/v3 from 3.8.0 to 3.9.0
Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/coreos/go-oidc/releases)
- [Commits](https://github.com/coreos/go-oidc/compare/v3.8.0...v3.9.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-oidc/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 19:35:48 -08:00
dependabot[bot] e5df7ab3ad Bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 19:35:26 -08:00
Kristof Mattei 0465f9b188 fix: tests for allow popups to escape sandbox 2023-12-10 16:59:58 -08:00
Kristof Mattei d53ad3b79a fix: clicking youtube links in iframes returns ERR_BLOCKED_BY_RESPONSE 2023-12-10 16:59:58 -08:00
Ole Bertram 698bea4ec8 Fix inaccessible metrics endpoint when listening on Unix socket 2023-12-06 19:52:33 -08:00
Jesse Jaggars 95039410b5 adding detailed error handling to the omnivore integration 2023-12-05 21:34:16 -08:00
Jesse Jaggars e933fb11e9
Add Omnivore integration 2023-12-04 20:05:04 -08:00
dependabot[bot] 0666d98648 Bump github.com/tdewolff/minify/v2 from 2.20.8 to 2.20.9
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.8 to 2.20.9.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.8...v2.20.9)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 19:08:50 -08:00
dependabot[bot] e314ae6df0 Bump github.com/go-webauthn/webauthn from 0.9.3 to 0.9.4
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.9.3 to 0.9.4.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.9.3...v0.9.4)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 19:08:40 -08:00
Shizun Ge bcb0978e9e improve scheduler tests.
Capture timeNow() before calculation next check at.
Check if the desired interval is set.
2023-12-03 15:01:19 -08:00
Shizun Ge f3f892f448 log nb_jobs only when number of jobs is larger than 0 in scheduler. 2023-12-03 14:57:20 -08:00
Frédéric Guillot 1af1bc3460 Google Reader API: Allow rename and move feed at the same time
Fixes #2191
2023-12-01 17:50:01 -08:00
Frédéric Guillot d0f99cee1a Regression: ensure all HTML documents are encoded in UTF-8
Fixes #2196
2023-12-01 16:52:03 -08:00
dependabot[bot] f8b40085cd Bump github.com/go-webauthn/webauthn from 0.9.2 to 0.9.3
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.9.2 to 0.9.3.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.9.2...v0.9.3)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-01 16:29:09 -08:00
Frédéric Guillot 5de0714256 Deduplicate feed URLs when parsing HTML document during discovery process
Fixes #2232
2023-12-01 13:57:05 -08:00
Shizun Ge bfa83cbf99 Calculate a virtual weekly count based on the average updating frequency. 2023-12-01 12:29:36 -08:00
Shizun Ge 27ec6dbd7d Setting NextCheckAt due to TTL of a feed in feed.go.
Add unit tests.
2023-12-01 12:22:30 -08:00
Shizun Ge a8daee60fb run linter and tests workflow on demand 2023-11-30 17:40:20 -08:00
Sam Crang fab423cca0 Use "starred" rather than "bookmarked"
This change replaces usages of "bookmarked" entries with "starred"
entries the latter which seems to be be used more prominently.
2023-11-29 19:54:18 -08:00
Shizun Ge 70b69ecd19 Add SCHEDULER_ROUND_ROBIN_MIN_INTERVAL
Separated from POLLING_FREQUENCY.
2023-11-29 19:52:14 -08:00
Thomas J Faughnan Jr fe0ef8b579 Fix conditional requests regression
The recent HTTP client refactor in 14e25ab9fe
caused feed refreshes to no longer make conditional requests. Prior to
the refactor, `client.WithCacheHeaders` handled this. Now this function
is split into `fetcher.WithETag` and `fetcher.WithLastModified` but
these functions are only declared and never actually used. Fix this by
calling them inside `handler.RefreshFeed`.
2023-11-29 19:46:50 -08:00
dependabot[bot] 586a04f812 Bump github.com/coreos/go-oidc/v3 from 3.7.0 to 3.8.0
Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/coreos/go-oidc/releases)
- [Commits](https://github.com/coreos/go-oidc/compare/v3.7.0...v3.8.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-oidc/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 19:45:26 -08:00
dependabot[bot] 293034bcde Bump github.com/go-webauthn/webauthn from 0.9.1 to 0.9.2
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.9.1 to 0.9.2.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.9.1...v0.9.2)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 19:36:53 -08:00
dependabot[bot] ac516a4824 Bump github.com/tdewolff/minify/v2 from 2.20.7 to 2.20.8
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.7 to 2.20.8.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.7...v2.20.8)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 19:33:14 -08:00
Shizun Ge 65e2fddfb5 Use variables for the status in the entries table 2023-11-29 19:32:36 -08:00
dependabot[bot] 2d167ae9f9 Bump golang.org/x/oauth2 from 0.14.0 to 0.15.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 16:25:00 -08:00
Shizun Ge 32779f596f Update Chinese (CN & TW) translation 2023-11-23 08:39:31 +01:00
Shizun Ge 273b96bfe0 Update Chinese(TW) translation 2023-11-23 08:39:31 +01:00
Shizun Ge 4ffc073153 add github links to about page.
Add github links about release and commit.
2023-11-23 08:38:05 +01:00
dependabot[bot] fc841beedd Bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-22 08:07:49 +01:00
dependabot[bot] 9f4723f8eb Bump github.com/go-webauthn/webauthn from 0.8.6 to 0.9.1
Bumps [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) from 0.8.6 to 0.9.1.
- [Release notes](https://github.com/go-webauthn/webauthn/releases)
- [Commits](https://github.com/go-webauthn/webauthn/compare/v0.8.6...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-webauthn/webauthn
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-21 18:21:05 +01:00
mrchi b37bb43e09 Update Chinese translation 2023-11-18 21:11:42 +01:00
Thomas J Faughnan Jr 7a03291442 Fix default User-Agent regression
The recent HTTP client refactor in 14e25ab9fe
introduced a bug in which the global default User-Agent is no longer
used for requests. Unless a per-feed User-Agent exists, the Go standard
library's default User-Agent is used, which looks something like
"Go-http-client/1.1". To fix this, make RequestBuilder.WithUserAgent
take an additional argument, the default User-Agent, which will be used
if there is no per-feed User-Agent (i.e. it is an empty string).

Fixes #2188
Fixes #2189
2023-11-18 20:57:47 +01:00
dependabot[bot] 14f70351c0 Bump github.com/tdewolff/minify/v2 from 2.20.6 to 2.20.7
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.6 to 2.20.7.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.6...v2.20.7)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-18 20:27:35 +01:00
Frédéric Guillot d195ce6340 Update ChangeLog 2023-11-12 17:14:47 +00:00
Frédéric Guillot e9480ba1da
Trigger build binaries workflow when pushing tags 2023-11-12 16:43:35 +01:00
Frédéric Guillot 1bd5d57884 `user/{userID}/state/com.google/read` is missing in `categories` section for read entries 2023-11-09 12:50:42 +01:00
dependabot[bot] 6544e413b8 Bump golang.org/x/oauth2 from 0.13.0 to 0.14.0
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-09 10:35:10 +01:00
Frédéric Guillot d7437f125b Improve error log message in worker 2023-11-08 19:58:56 +00:00
Frédéric Guillot f2849ca00f Improve WebAuthn buttons layout 2023-11-08 20:23:17 +01:00
Frédéric Guillot aa3dc574a7 Google Reader API: Take ExcludeTargets into consideration in Feed stream handler 2023-11-08 17:31:05 +01:00
Frédéric Guillot ba65556eac Show number of visible entries instead of number of read entries in feed list 2023-11-08 16:34:27 +01:00
Nick Parker 2bc5ad53c2 Avoid long duration strings: round to nearest second
For example, seeing "Next check: 14m56.245483933s" in feeds list after force-refreshing a feed.

This rounds to the nearest second, so it'll instead be "14m56s"

Other examples from latter two test cases:
- "12.345678s" -> "12s"
- "1m27.654321s" -> "1m28s"
2023-11-08 14:19:30 +01:00
Frédéric Guillot bc317cfcd1 OIDC: Redirect to user home page after successful authentication 2023-11-07 21:21:56 +01:00
dependabot[bot] 00a64710c2 Bump github.com/gorilla/mux from 1.8.0 to 1.8.1
Bumps [github.com/gorilla/mux](https://github.com/gorilla/mux) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/gorilla/mux/releases)
- [Commits](https://github.com/gorilla/mux/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/gorilla/mux
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-07 11:18:02 +01:00
Frédéric Guillot ba614af82d Disable WebAuthn by default because it requires to configure the BASE_URL 2023-11-06 20:51:19 +01:00
Frédéric Guillot 2b8342fcd5 Refactor WebAuthn Javascript code 2023-11-06 19:55:32 +01:00
Frédéric Guillot a75256bed5 Add Passkeys French translations 2023-11-05 19:00:34 +00:00
Frédéric Guillot 0cc369a76e Add WEBAUTHN config option to the man page 2023-11-05 18:37:56 +00:00
Florian Rüchel 62ef8ed57a
Add WebAuthn / Passkey integration
This is a rebase of #1618 in which @dave-atx added WebAuthn support.

Closes #1618
2023-11-05 18:57:35 +01:00
dependabot[bot] 62188b49f0 Bump github.com/tdewolff/minify/v2 from 2.20.5 to 2.20.6
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.5 to 2.20.6.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.5...v2.20.6)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-03 17:00:57 +01:00
Frédéric Guillot 305d5ad7b6 Add github-cli and docker-outside-of-docker to devcontainer 2023-11-02 14:31:54 +00:00
Frédéric Guillot d8c82829c4
Add GitHub workflow to build binaries 2023-11-01 21:04:32 +01:00
Frédéric Guillot e3eaaea15a Update date parser to parse more invalid date formats 2023-11-01 20:55:35 +01:00
Frédéric Guillot 500c60b807 Fix error handling and logging issue after refactoring 2023-11-01 19:59:12 +01:00
James Loh ef53bf14ae Add Category ID to webhooks
My use case for this is I want to ignore some webhooks based on the category the feed is in
2023-11-01 18:02:14 +01:00
dependabot[bot] 25f9ca2eeb Bump github.com/tdewolff/minify/v2 from 2.20.4 to 2.20.5
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.4 to 2.20.5.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.4...v2.20.5)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 17:42:23 +01:00
Nicolas Martinelli d566dea265 Fix category hide_globally property in `/entries`
Follow-up of 64c4c6b347
2023-10-31 16:57:03 +01:00
dependabot[bot] c81b61462e Bump github.com/tdewolff/minify/v2 from 2.19.10 to 2.20.4
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.19.10 to 2.20.4.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.19.10...v2.20.4)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-31 15:34:04 +01:00
dependabot[bot] 1fe362ecd0 Bump github.com/yuin/goldmark from 1.5.6 to 1.6.0
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.5.6 to 1.6.0.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.5.6...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-31 14:14:07 +01:00
Nicholas Parker 257e8c4761 Allow iframes pointing to Twitch videos
Docs: https://dev.twitch.tv/docs/embed/video-and-clips/#non-interactive-inline-frames-for-live-streams-and-vods
2023-10-27 10:02:57 -07:00
Tianfeng Wang a1537f4b0d
Filter feed entries based on url or title 2023-10-25 19:38:08 -07:00
Frédéric Guillot eeaab72a9f Refactor feed discovery and avoid an extra HTTP request if the url provided is the feed 2023-10-22 18:05:37 -07:00
Frédéric Guillot 14e25ab9fe Refactor HTTP Client and LocalizedError packages 2023-10-22 13:09:30 -07:00
Ryan Stafford 120aabfbce
Add RSS-Bridge integration 2023-10-22 11:10:56 -07:00
Frédéric Guillot 5e6c054345 Take RSS TTL field into consideration to schedule next check date 2023-10-20 20:11:05 -07:00
dependabot[bot] ed35555d74 Bump github.com/coreos/go-oidc/v3 from 3.6.0 to 3.7.0
Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/coreos/go-oidc/releases)
- [Commits](https://github.com/coreos/go-oidc/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: github.com/coreos/go-oidc/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-20 16:34:50 -07:00
dependabot[bot] 619584b5f6 Bump github.com/tdewolff/minify/v2 from 2.12.9 to 2.19.10
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.12.9 to 2.19.10.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.12.9...v2.19.10)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-20 16:33:55 -07:00
Frédéric Guillot 4cc99881d8 Refactor Batch Builder and prevent accidental and excessive refreshes from the web ui 2023-10-20 16:07:18 -07:00
Frédéric Guillot 95ee1c423b Change log level to warning for failed feeds refresh in cronjob 2023-10-20 14:02:17 -07:00
Frédéric Guillot ff204d67b9 Add GitHub extensions to dev container 2023-10-19 21:59:10 -07:00
Frédéric Guillot 5ac3489ee5 Do not log website without icon as warning 2023-10-19 20:36:51 -07:00
Frédéric Guillot 9fd2dfa680 Refactor icon finder
Changes:

- Continue the discovery process when the feed icon is invalid
- Search all icons from the HTML document and do not stop on the first one
2023-10-18 22:24:56 -07:00
Frédéric Guillot 7650c81ad9 Add support for SVG icons with data url without encoding 2023-10-18 20:46:46 -07:00
Frédéric Guillot c60b3f52a5 Add new page for background feeds refresh 2023-10-18 20:26:33 -07:00
Frédéric Guillot 23d2cfe0f9 Expose `next_check_at` in the web ui and API 2023-10-17 21:25:41 -07:00
Frédéric Guillot 5dc44453ba Add indexes to improve performance 2023-10-17 20:18:49 -07:00
mcnesium 6086899b28 When building the docker image, make sure to pull the latest base image.
Fixes #2131 #2132
2023-10-17 19:01:32 -07:00
Frédéric Guillot 2842017b59 Strip version prefix when building Debian package 2023-10-17 09:26:43 -07:00
Frédéric Guillot cc44d14722 Avoid excessive manual polling with default scheduler 2023-10-16 21:41:18 -07:00
338 changed files with 25432 additions and 18999 deletions

View File

@ -7,6 +7,10 @@
"forwardPorts": [
8080
],
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"customizations": {
"vscode": {
"settings": {
@ -17,7 +21,10 @@
"extensions": [
"ms-azuretools.vscode-docker",
"golang.go",
"rangav.vscode-thunder-client"
"rangav.vscode-thunder-client",
"GitHub.codespaces",
"GitHub.copilot",
"GitHub.copilot-chat"
]
}
}

View File

@ -1,7 +1,7 @@
version: '3.8'
services:
app:
image: mcr.microsoft.com/devcontainers/go
image: mcr.microsoft.com/devcontainers/go:1.22
volumes:
- ..:/workspace:cached
command: sleep infinity
@ -24,7 +24,7 @@ services:
ports:
- 5432:5432
apprise:
image: caronc/apprise:latest
image: caronc/apprise:1.0
restart: unless-stopped
hostname: apprise
volumes:

View File

@ -1,10 +0,0 @@
---
name: Improvement
about: Do you have an idea to improve an existing feature?
title: ''
labels: improvements
assignees: ''
---

View File

@ -1,2 +0,0 @@
paths-ignore:
- ./http/client/testdata

View File

@ -1,4 +1,7 @@
Do you follow the guidelines?
- [ ] I have tested my changes
- [ ] There is no breaking changes
- [ ] I really tested my changes and there is no regression
- [ ] Ideally, my commit messages use the same convention as the Go project: https://go.dev/doc/contribute#commit_messages
- [ ] I read this document: https://miniflux.app/faq.html#pull-request

29
.github/workflows/build_binaries.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Build Binaries
on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Golang
uses: actions/setup-go@v5
with:
go-version: "1.22.x"
check-latest: true
- name: Checkout
uses: actions/checkout@v4
- name: Compile binaries
env:
CGO_ENABLED: 0
run: make build
- name: Upload binaries
uses: actions/upload-artifact@v4
with:
name: binaries
path: miniflux-*
if-no-files-found: error
retention-days: 5

View File

@ -29,14 +29,15 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
- uses: actions/setup-go@v5
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/config.yml
go-version: "1.22.x"
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

@ -1,6 +1,7 @@
name: Debian Packages
permissions: read-all
on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
@ -28,8 +29,34 @@ jobs:
run: make debian-packages
- name: List generated files
run: ls -l *.deb
build-packages-manually:
if: github.event_name != 'pull_request' && github.event_name != 'push'
name: Build Packages Manually
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
with:
install: true
- name: Available Docker Platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Build Debian Packages
run: make debian-packages
- name: Upload package
uses: actions/upload-artifact@v4
with:
name: packages
path: "*.deb"
if-no-files-found: error
retention-days: 3
publish-packages:
if: ${{ ! github.event.pull_request }}
if: github.event_name == 'push'
name: Publish Packages
runs-on: ubuntu-latest
steps:

View File

@ -8,35 +8,8 @@ on:
pull_request:
branches: [ main ]
jobs:
test-docker-images:
if: github.event.pull_request
name: Test Images
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Alpine image
uses: docker/build-push-action@v5
with:
context: .
file: ./packaging/docker/alpine/Dockerfile
push: false
tags: ${{ github.repository_owner }}/miniflux:alpine-dev
- name: Test Alpine Docker image
run: docker run --rm ${{ github.repository_owner }}/miniflux:alpine-dev miniflux -i
- name: Build Distroless image
uses: docker/build-push-action@v5
with:
context: .
file: ./packaging/docker/distroless/Dockerfile
push: false
tags: ${{ github.repository_owner }}/miniflux:distroless-dev
- name: Test Distroless Docker image
run: docker run --rm ${{ github.repository_owner }}/miniflux:distroless-dev miniflux -i
publish-docker-images:
if: ${{ ! github.event.pull_request }}
name: Publish Images
docker-images:
name: Docker Images
permissions:
packages: write
runs-on: ubuntu-latest
@ -46,33 +19,33 @@ jobs:
with:
fetch-depth: 0
- name: Generate Alpine Docker tag
id: docker_alpine_tag
run: |
DOCKER_IMAGE=${{ github.repository_owner }}/miniflux
DOCKER_VERSION=dev
if [ "${{ github.event_name }}" = "schedule" ]; then
DOCKER_VERSION=nightly
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
elif [[ $GITHUB_REF == refs/tags/* ]]; then
DOCKER_VERSION=${GITHUB_REF#refs/tags/}
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION},docker.io/${DOCKER_IMAGE}:latest,ghcr.io/${DOCKER_IMAGE}:latest,quay.io/${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
- name: Generate Alpine Docker tags
id: docker_alpine_tags
uses: docker/metadata-action@v5
with:
images: |
docker.io/${{ github.repository_owner }}/miniflux
ghcr.io/${{ github.repository_owner }}/miniflux
quay.io/${{ github.repository_owner }}/miniflux
tags: |
type=ref,event=pr
type=schedule,pattern=nightly
type=semver,pattern={{raw}}
- name: Generate Distroless Docker tag
id: docker_distroless_tag
run: |
DOCKER_IMAGE=${{ github.repository_owner }}/miniflux
DOCKER_VERSION=dev-distroless
if [ "${{ github.event_name }}" = "schedule" ]; then
DOCKER_VERSION=nightly-distroless
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
elif [[ $GITHUB_REF == refs/tags/* ]]; then
DOCKER_VERSION=${GITHUB_REF#refs/tags/}-distroless
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION},docker.io/${DOCKER_IMAGE}:latest-distroless,ghcr.io/${DOCKER_IMAGE}:latest-distroless,quay.io/${DOCKER_IMAGE}:latest-distroless"
fi
echo ::set-output name=tags::${TAGS}
- name: Generate Distroless Docker tags
id: docker_distroless_tags
uses: docker/metadata-action@v5
with:
images: |
docker.io/${{ github.repository_owner }}/miniflux
ghcr.io/${{ github.repository_owner }}/miniflux
quay.io/${{ github.repository_owner }}/miniflux
tags: |
type=ref,event=pr
type=schedule,pattern=nightly
type=semver,pattern={{raw}}
flavor: |
suffix=-distroless,onlatest=true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@ -81,12 +54,14 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
@ -94,6 +69,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Quay Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: quay.io
@ -106,8 +82,8 @@ jobs:
context: .
file: ./packaging/docker/alpine/Dockerfile
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
push: true
tags: ${{ steps.docker_alpine_tag.outputs.tags }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_alpine_tags.outputs.tags }}
- name: Build and Push Distroless images
uses: docker/build-push-action@v5
@ -115,5 +91,5 @@ jobs:
context: .
file: ./packaging/docker/distroless/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.docker_distroless_tag.outputs.tags }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_distroless_tags.outputs.tags }}

View File

@ -5,6 +5,7 @@ on:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
jshint:
@ -12,20 +13,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install jshint
- name: Install linters
run: |
sudo npm install -g jshint@2.13.3
sudo npm install -g jshint@2.13.6 eslint@8.57.0
- name: Run jshint
run: jshint ui/static/js/*.js
run: jshint internal/ui/static/js/*.js
- name: Run ESLint
run: eslint internal/ui/static/js/*.js
golangci:
name: Golang Linter
name: Golang Linters
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: "1.21"
- uses: golangci/golangci-lint-action@v3
go-version: "1.22.x"
- run: "go vet ./..."
- uses: golangci/golangci-lint-action@v6
with:
args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace
args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace --enable gocritic
- uses: dominikh/staticcheck-action@v1.3.1
with:
version: "2023.1.7"
install-go: false

View File

@ -1,6 +1,7 @@
name: RPM Packages
permissions: read-all
on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
@ -19,8 +20,25 @@ jobs:
run: make rpm
- name: List generated files
run: ls -l *.rpm
build-package-manually:
if: github.event_name != 'pull_request' && github.event_name != 'push'
name: Build Packages Manually
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build RPM Package
run: make rpm
- name: Upload package
uses: actions/upload-artifact@v4
with:
name: packages
path: "*.rpm"
if-no-files-found: error
retention-days: 3
publish-package:
if: ${{ ! github.event.pull_request }}
if: github.event_name == 'push'
name: Publish Packages
runs-on: ubuntu-latest
steps:

View File

@ -5,6 +5,7 @@ on:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
unit-tests:
@ -14,10 +15,10 @@ jobs:
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
go-version: ["1.21"]
go-version: ["1.22.x"]
steps:
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Checkout
@ -40,9 +41,9 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.22.x"
- name: Checkout
uses: actions/checkout@v4
- name: Install Postgres client

334
ChangeLog
View File

@ -1,3 +1,335 @@
Version 2.1.3 (April 27, 2024)
------------------------------
* `api`: `rand.Intn(math.MaxInt64)` causes tests to fail on 32-bit architectures (use `rand.Int()` instead)
* `ci`: use `docker/metadata-action` instead of deprecated shell-scripts
* `database`: remove `entries_feed_url_idx` index because entry URLs can exceeds btree index size limit
* `finder`: find feeds from YouTube playlist
* `http/response`: add brotli compression support
* `integration/matrix`: fix function name in comment
* `packaging`: specify container registry explicitly (e.g., Podman does not use `docker.io` by default)
* `packaging`: use `make miniflux` instead of duplicating `go build` arguments (this leverages Go's PIE build mode)
* `reader/fetcher`: add brotli content encoding support
* `reader/processor`: minimize feed entries HTML content
* `reader/rewrite`: add a rule for `oglaf.com`
* `storage`: change `GetReadTime()` function to use `entries_feed_id_hash_key` index
* `ui`: add seek and speed controls to media player
* `ui`: add tag entries page
* `ui`: fix JavaScript error when clicking on unread counter
* `ui`: use `FORCE_REFRESH_INTERVAL` config for category refresh
* Bump `github.com/tdewolff/minify/v2` from `2.20.19` to `2.20.20`
* Bump `golang.org/x/net` from `0.22.0` to `0.24.0`
* Bump `golang.org/x/term` from `0.18.0` to `0.19.0`
* Bump `golang.org/x/oauth2` from `0.18.0` to `0.19.0`
* Bump `github.com/yuin/goldmark` from `1.7.0` to `1.7.1`
Version 2.1.2 (March 30, 2024)
------------------------------
* `api`: rewrite API integration tests without build tags
* `ci`: add basic ESLinter checks
* `ci`: enable go-critic linter and fix various issues detected
* `ci`: fix JavaScript linter path in GitHub Actions
* `cli`: avoid misleading error message when creating an admin user automatically
* `config`: add `FILTER_ENTRY_MAX_AGE_DAYS` option
* `config`: bump the number of simultaneous workers
* `config`: rename `PROXY_*` options to `MEDIA_PROXY_*`
* `config`: use `crypto.GenerateRandomBytes` instead of doing it by hand
* `http/request`: refactor conditions to be more idiomatic
* `http/response`: remove legacy `X-XSS-Protection` header
* `integration/rssbrige`: fix rssbrige import
* `integration/shaarli`: factorize the header+payload concatenation as data
* `integration/shaarli`: no need to base64-encode then remove the padding when we can simply encode without padding
* `integration/shaarli`: the JWT token was declared as using HS256 as algorithm, but was using HS512
* `integration/webhook`: add category title to request body
* `locale`: update Turkish translations
* `man page`: sort config options in alphabetical order
* `mediaproxy`: reduce the internal indentation of `ProxifiedUrl` by inverting some conditions
* `mediaproxy`: simplify and refactor the package
* `model`: replace` Optional{Int,Int64,Float64}` with a generic function `OptionalNumber()`
* `model`: use struct embedding for `FeedCreationRequestFromSubscriptionDiscovery` to reduce code duplication
* `reader/atom`: avoid debug message when the date is empty
* `reader/atom`: change `if !a { a = } if !a {a = }` constructs into `if !a { a = ; if !a {a = }}` to reduce the number of comparisons and improve readability
* `reader/atom`: Move the population of the feed's entries into a new function, to make BuildFeed easier to understand/separate concerns/implementation details
* `reader/atom`: refactor Atom parser to use an adapter
* `reader/atom`: use `sort+compact` instead of `compact+sort` to remove duplicates
* `reader/atom`: when detecting the format, detect its version as well
* `reader/encoding`: inline a one-liner function
* `reader/handler`: fix force refresh feature
* `reader/json`: refactor JSON Feed parser to use an adapter
* `reader/media`: remove a superfluous error-check: `strconv.ParseInt` returns `0` when passed an empty string
* `reader/media`: simplify switch-case by moving a common condition above it
* `reader/processor`: compile block/keep regex only once per feed
* `reader/rdf`: refactor RDF parser to use an adapter
* `reader/rewrite`: inline some one-line functions
* `reader/rewrite`: simplify `removeClickbait`
* `reader/rewrite`: transform a free-standing function into a method
* `reader/rewrite`: use a proper constant instead of a magic number in `applyFuncOnTextContent`
* `reader/rss`: add support for `<media:category>` element
* `reader/rss`: don't add empty tags to RSS items
* `reader/rss`: refactor RSS parser to use a default namespace to avoid some limitations of the Go XML parser
* `reader/rss`: refactor RSS Parser to use an adapter
* `reader/rss`: remove some duplicated code in RSS parser
* `reader`: ensure that enclosure URLs are always absolute
* `reader`: move iTunes and GooglePlay XML definitions to their own packages
* `reader`: parse podcast categories
* `reader`: remove trailing space in `SiteURL` and `FeedURL`
* `storage`: do not store empty tags
* `storage`: simplify `removeDuplicates()` to use a `sort`+`compact` construct instead of doing it by hand with a hashmap
* `storage`: Use plain strings concatenation instead of building an array and then joining it
* `timezone`: make sure the tests pass when the timezone database is not installed on the host
* `ui/css`: align `min-width` with the other `min-width` values
* `ui/css`: fix regression: "Add to Home Screen" button is unreadable
* `ui/js`: don't use lambdas to return a function, use directly the function instead
* `ui/js`: enable trusted-types
* `ui/js`: fix download button loading label
* `ui/js`: fix JavaScript error on the login page when the user not authenticated
* `ui/js`: inline one-line functions
* `ui/js`: inline some `querySelectorAll` calls
* `ui/js`: reduce the scope of some variables
* `ui/js`: remove a hack for "Chrome 67 and earlier" since it was released in 2018
* `ui/js`: replace `DomHelper.findParent` with `.closest`
* `ui/js`: replace `let` with `const`
* `ui/js`: simplify `DomHelper.getVisibleElements` by using a `filter` instead of a loop with an index
* `ui/js`: use a `Set` instead of an array in a `KeyboardHandler`'s member
* `ui/js`: use some ternaries where it makes sense
* `ui/static`: make use of `HashFromBytes` everywhere
* `ui/static`: set minifier ECMAScript version
* `ui`: add keyboard shortcuts for scrolling to top/bottom of the item list
* `ui`: add media player control playback speed
* `ui`: remove unused variables and improve JSON decoding in `saveEnclosureProgression()`
* `validator`: display an error message on edit feed page when the feed URL is not unique
* Bump `github.com/coreos/go-oidc/v3` from `3.9.0` to `3.10.0`
* Bump `github.com/go-webauthn/webauthn` from `0.10.1` to `0.10.2`
* Bump `github.com/tdewolff/minify/v2` from `2.20.18` to `2.20.19`
* Bump `google.golang.org/protobuf` from `1.32.0` to `1.33.0`
Version 2.1.1 (March 10, 2024)
-----------------------------
* Move search form to a dedicated page
* Add Readeck integration
* Add feed option to disable HTTP/2 to avoid fingerprinting
* Add `Enter` key as a hotkey to open selected item
* Proxify `video` element `poster` attribute
* Add a couple of new possible locations for feeds
* Hugo likes to generate `index.xml`
* `feed.atom` and `feed.rss` are used by enterprise-scale/old-school gigantic CMS
* Fix categories import from Thunderbird's OPML
* Fix logo misalignment when using languages that are more verbose than English
* Google Reader: Do not return a 500 error when no items is returned
* Handle RDF feeds with duplicated `<title>` elements
* Sort integrations alphabetically
* Add more URL validation in media proxy
* Add unit test to ensure each translation has the correct number of plurals
* Add missing plurals for some languages
* Makefile: quiet `git describe` and `rev-parse` stderr: When building from a tarball instead of a cloned git repo, there would be two `fatal: not a git repository` errors emitted even though the build succeeds. This is because of how `VERSION` and `COMMIT` are set in the Makefile. This PR suppresses the stderr for these variable assignments.
* Makefile: do not force `CGO_ENABLED=0` for `miniflux` target
* Add GitHub Action pipeline to build packages on-demand
* Remove Golint (deprecated), use `staticcheck` and `golangci-lint` instead
* Build amd64/arm64 Debian packages with CGO disabled
* Update `go.mod` and add `.exe` suffix to Windows binary
* Add a couple of fuzzers
* Fix CodeQL workflow
* Code and performance improvements:
* Use an `io.ReadSeeker` instead of an `io.Reader` to parse feeds
* Speed up the sanitizer:
- Allow Youtube URLs to start with `www`
- Use `strings.Builder` instead of a `bytes.Buffer`
- Use a `strings.NewReader` instead of a `bytes.NewBufferString`
- Sprinkles a couple of `continue` to make the code-flow more obvious
- Inline calls to `inList`, and put their parameters in the right order
- Simplify `isPixelTracker`
- Simplify `isValidIframeSource`, by extracting the hostname and comparing it directly, instead of using the full url and checking if it starts with multiple variations of the same one (`//`, `http:`, `https://` multiplied by `/www.`)
- Add a benchmark
- Instead of having to allocate a ~100 keys map containing possibly dynamic values (at least to the go compiler), allocate it once in a global variable. This significantly speeds things up, by reducing the garbage
- Use constant time access for maps instead of iterating on them
- Build a ~large whitelist map inline instead of constructing it item by item (and remove a duplicate key/value pair)
- Use `slices` instead of hand-rolled loops
collector/allocator involvements.
* Reuse a `Reader` instead of copying to a buffer when parsing an Atom feed
* Preallocate memory when exporting to OPML: This should marginally increase performance when exporting a large amount of feeds to OPML
* Delay call of `view.New` after logging the user in: There is no need to do extra work like creating a session and its associated view until the user has been properly identified and as many possibly-failing sql request have been successfully run
* Use constant-time comparison for anti-csrf tokens: This is probably completely overkill, but since anti-csrf tokens are secrets, they should be compared against untrusted inputs in constant time
* Simplify and optimize `genericProxyRewriter`
- Reduce the amount of nested loops: it's preferable to search the whole page once and filter on it (even with filters that should always be false), than searching it again for every element we're looking for.
- Factorize the proxying conditions into a `shouldProxy` function to reduce the copy-pasta.
* Speed up `removeUnlikelyCandidates`: `.Not` returns a brand new `Selection`, copied element by element
* Improve `EstimateReadingTime`'s speed by a factor 7
- Refactorise the tests and add some
- Use 250 signs instead of the whole text
- Only check for Korean, Chinese and Japanese script
- Add a benchmark
- Use a more idiomatic control flow
* Don't compute reading-time when unused: If the user doesn't display reading times, there is no need to compute them. This should speed things up a bit, since `whatlanggo.Detect` is abysmally slow.
* Simplify `username` generation for the integration tests: No need to generate random numbers 10 times, generate a single big-enough one. A single int64 should be more than enough
* Add missing regex anchor detected by CodeQL
* Don't mix up slices capacity and length
* Use prepared statements for intervals, `ArchiveEntries` and `updateEnclosures`
* Use modern for-loops introduced with Go 1.22
* Remove a superfluous condition: No need to check if the length of `line` is positive since we're checking afterwards that it contains the `=` sign
* Close resources as soon as possible, instead of using `defer()` in a loop
* Remove superfluous escaping in a regex
* Use `strings.ReplaceAll` instead of `strings.Replace(…, -1)`
* Use `strings.EqualFold` instead of `strings.ToLower(…) ==`
* Use `.WriteString(` instead of `.Write([]byte(…`
* Use `%q` instead of `"%s"`
* Make `internal/worker/worker.go` read-only
* Use a switch-case construct in `internal/locale/plural.go` instead of an avalanche of `if`
* Template functions: simplify `formatFileSize` and `duration` implementation
* Inline some templating functions
* Make use of `printer.Print` when possible
* Add a `printer.Print` to `internal/locale/printer.go`: No need to use variadic functions with string format interpolation to generate static strings
* Minor code simplification in `internal/ui/view/view.go`: No need to create the map item by item when we can create it in one go
* Build the map inline in `CountAllFeeds()`: No need to build an empty map to then add more fields in it one by one
* Miscellaneous improvements to `internal/reader/subscription/finder.go`:
- Surface `localizedError` in `FindSubscriptionsFromWellKnownURLs` via `slog`
- Use an inline declaration for new subscriptions, like done elsewhere in the
file, if only for consistency's sake
- Preallocate the `subscriptions` slice when using an RSS-bridge,
* Use an update-where for `MarkCategoryAsRead` instead of a subquery
* Simplify `CleanOldUserSessions`' query: No need for a subquery, filtering on `created_at` directly is enough
* Simplify `cleanupEntries`' query
- `NOT (hash=ANY(%4))` can be expressed as `hash NOT IN $4`
- There is no need for a subquery operating on the same table, moving the conditions out is equivalent.
* Reformat `ArchiveEntries`'s query for consistency's sake and replace the `=ANY` with an `IN`
* Reformat the query in `GetEntryIDs` and `GetReadTime`'s query for consistency's sake
* Simplify `WeeklyFeedEntryCount`: No need for a `BETWEEN`: we want to filter on entries published in the last week, no need to express is as "entries published between now and last week", "entries published after last week" is enough
* Add some tests for `add_image_title`
* Remove `github.com/google/uuid` dependencies: Replace it with a hand-rolled implementation. Heck, an UUID isn't even a requirement according to Omnivore API docs
* Simplify `internal/reader/icon/finder.go`:
- Use a simple regex to parse data uri instead of a hand-rolled parser, and document what fields are considered mandatory.
- Use case-insensitive matching to find (fav)icons, instead of doing the same query twice with different letter cases
- Add `apple-touch-icon-precomposed.png` as a fallback `favicon`
- Reorder the queries to have `icon` first, since it seems to be the most popular one. It used to be last, meaning that pages had to be parsed completely 4 times, instead of one now.
- Minor factorisation in `findIconURLsFromHTMLDocument`
* Small refactoring of `internal/reader/date/parser.go`:
- Split dates formats into those that require local times and those who don't, so that there is no need to have a switch-case in the for loop with around 250 iterations at most.
- Be more strict when it comes to timezones, previously invalid ones like -13 were accepted. Also add a test for this.
- Bail out early if the date is an empty string.
* Make use of Go ≥ 1.21 slices package instead of hand-rolled loops
* Reorder the fields of the `Entry` struct to save some memory
* Dependencies update:
* Bump `golang.org/x/oauth2` from `0.17.0` to `0.18.0`
* Bump `github.com/prometheus/client_golang` from `1.18.0` to `1.19.0`
* Bump `github.com/tdewolff/minify/v2` from `2.20.16` to `2.20.18`
* Bump `github.com/PuerkitoBio/goquery` from `1.8.1` to `1.9.1`
* Bump `golang.org/x/crypto` from `0.19.0` to `0.20.0`
* Bump `github.com/go-jose/go-jose/v3` from `3.0.1` to `3.0.3`
Version 2.1.0 (February 17, 2024)
---------------------------------
* Add Linkwarden integration
* Add LinkAce integration
* Add `FORCE_REFRESH_INTERVAL` config option
* Add `item-meta-info-reading-time` CSS class
* Add `add_dynamic_iframe` rewrite function
* Add attribute `data-original-mos` to `add_dynamic_image` rewrite candidates
* Update entry processor to allow blocking/keeping entries by tags and/or authors
* Change default `Accept` header when fetching feeds
* Rewrite relative RSS Bridge URL to absolute URL
* Use numeric user ID in Alpine and distroless container image (avoid `securityContext` error in Kubernetes)
* Always try to use HTTP/2 when fetching feeds if available
* Add `type` attribute in OPML export as per OPML 2.0 specs
* Fix missing translation argument for the key `error.unable_to_parse_feed`
* Fix Debian package builder when using Go 1.22 and `armhf` architecture
* Fix typo in log message
* Fix incorrect label shown when saving an article
* Fix incorrect condition in refresh feeds cli
* Fix incorrect label `for` attribute
* Add missing label ID for custom CSS field
* Accessibility improvements:
* Add workaround for macOS VoiceOver that didn't announce `details` and `summary` when expanded
* Add `alert` role to alert message element
* Add a `h2` heading to the article element so that the screen reader users can navigate the article through the heading level
* Add an `aria-label` attribute for the article element for screen readers
* Remove the icon image `alt` attribute in feeds list to prevent screen reader to announce it before entry title
* Add `sr-only` CSS class for screen reader users (provides more context)
* Differentiate between buttons and links
* Change links that could perform actions to buttons
* Improve translation of hidden Aria elements
* Remove the redundant article role
* Add a search landmark for the search form so that the screen reader users can navigate to it
* Add skip to content link
* Add `nav` landmark to page header links
* Limit feed/category entry pagination to unread entries when coming from unread entry list
* Update German translation
* Update GitHub Actions to Go 1.22
* Bump `golang.org/x/term` from `0.16.0` to `0.17.0`
* Bump `github.com/google/uuid` from `1.5.0` to `1.6.0`
* Bump `github.com/yuin/goldmark` from `1.6.0` to `1.7.0`
* Bump `golang.org/x/oauth2` from `0.15.0` to `0.17.0`
* Bump `github.com/tdewolff/minify/v2` from `2.20.10` to `2.20.12`
* Bump `golang.org/x/term` from `0.15.0` to `0.16.0`
* Bump `github.com/prometheus/client_golang` from `1.17.0` to `1.18.0`
* Bump `github.com/tdewolff/minify/v2` from `2.20.9` to `2.20.16`
* Bump `golang.org/x/crypto` from `0.16.0` to `0.19.0`
* Bump `github.com/go-webauthn/webauthn` from `0.9.4` to` 0.10.1`
* Bump `golang.org/x/net` from `0.20.0` to `0.21.0`
Version 2.0.51 (December 13, 2023)
----------------------------------
* Add Omnivore integration
* Fixes for the regressions introduced in version 2.0.50:
* Ensure all HTML documents are encoded in UTF-8
* Send default User-Agent and HTTP caching headers when making HTTP requests
* Allow Youtube links to be opened outside the `iframe` (avoid `ERR_BLOCKED_BY_RESPONSE` error)
* Fix inaccessible metrics endpoint when listening on Unix socket
* Allow renaming and moving feed at the same time in the Google Reader API
* Log `nb_jobs` only when number of jobs is larger than 0 in background scheduler
* Deduplicate feed URLs when parsing HTML document during discovery process
* Calculate a virtual weekly count based on the average updating frequency (`POLLING_SCHEDULER=entry_frequency`)
* Update GitHub Actions workflow to be able to run the linter and tests on-demand
* Add `SCHEDULER_ROUND_ROBIN_MIN_INTERVAL` config option
* Add links to GitHub for the commit hash and the version in the about page
* Use "starred" rather than "bookmarked" in English translation
* Update Chinese (CN & TW) translation
* Bump `github.com/google/uuid` from `1.4.0` to `1.5.0`
* Bump `github.com/coreos/go-oidc/v3` from `3.7.0` to `3.9.0`
* Bump `github.com/tdewolff/minify/v2` from `2.20.6` to `2.20.9`
* Bump `github.com/go-webauthn/webauthn` from `0.8.6` to `0.9.4`
* Bump `golang.org/x/oauth2` from `0.14.0` to `0.15.0`
Version 2.0.50 (November 12, 2023)
----------------------------------
* Add WebAuthn / Passkey integration
* Add RSS-Bridge integration
* Take RSS TTL field into consideration to schedule next check date
* Show number of visible entries instead of number of read entries in feed list
* OpenID Connect: Redirect to configured user home page after successful authentication
* Google Reader API fixes:
* `user/{userID}/state/com.google/read` is missing in categories section for read entries
* Take `ExcludeTargets` into consideration in feed stream handler
* Allow iframes pointing to Twitch videos
* Filter feed entries based on URL or title
* Take into consideration `hide_globally` property defined for categories in `/v1/entries` API endpoint
* Add category ID to webhooks request body
* Update date parser to parse more invalid date formats
* Refactor feed discovery handler, and avoid an extra HTTP request if the URL provided is the feed
* Refactor HTTP Client and `LocalizedError` packages
* Refactor Batch Builder, and prevent accidental and excessive refreshes from the web UI
* Refactor icon finder:
- Continue the discovery process when the feed icon is invalid
- Search all icons from the HTML document and do not stop on the first one
* Add support for SVG icons with data URL without encoding
* Expose `next_check_at` in the web ui and API
* Add database indexes to improve performance
* Change log level to warning for failed feeds refresh in cronjob
* Do not log website without icon as warning
* Add GitHub workflow to build binaries
* Add GitHub extensions to devcontainer
* Make sure to pull the latest base image when building the Docker image
* Strip version prefix when building Debian package
* Add `github-cli` and `docker-outside-of-docker` features to devcontainer
* Bump `golang.org/x/*` dependencies
* Bump `github.com/gorilla/mux` from `1.8.0` to `1.8.1`
* Bump `github.com/tdewolff/minify/v2` from `2.19.9` to `2.20.6`
* Bump `github.com/yuin/goldmark` from `1.5.6` to `1.6.0`
* Bump `github.com/coreos/go-oidc/v3` from `3.6.0` to `3.7.0`
Version 2.0.49 (October 15, 2023)
---------------------------------
@ -96,7 +428,7 @@ Version 2.0.47 (August 20, 2023)
* Add new API endpoint: `/entries/{entryID}/save`
* Trigger Docker and packages workflows only for semantic tags
* Go module versioning expect Git tags to start with the letter v.
* The goal is to keep the existing naming convention for generated artifacts and
* The goal is to keep the existing naming convention for generated artifacts and
have proper versioning for the Go module.
* Bump `golang.org/x/*` dependencies
* Bump `github.com/yuin/goldmark`

View File

@ -1,17 +1,18 @@
APP := miniflux
DOCKER_IMAGE := miniflux/miniflux
VERSION := $(shell git describe --tags --abbrev=0)
COMMIT := $(shell git rev-parse --short HEAD)
BUILD_DATE := `date +%FT%T%z`
LD_FLAGS := "-s -w -X 'miniflux.app/v2/internal/version.Version=$(VERSION)' -X 'miniflux.app/v2/internal/version.Commit=$(COMMIT)' -X 'miniflux.app/v2/internal/version.BuildDate=$(BUILD_DATE)'"
PKG_LIST := $(shell go list ./... | grep -v /vendor/)
DB_URL := postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable
DEB_IMG_ARCH := amd64
APP := miniflux
DOCKER_IMAGE := miniflux/miniflux
VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null)
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
BUILD_DATE := `date +%FT%T%z`
LD_FLAGS := "-s -w -X 'miniflux.app/v2/internal/version.Version=$(VERSION)' -X 'miniflux.app/v2/internal/version.Commit=$(COMMIT)' -X 'miniflux.app/v2/internal/version.BuildDate=$(BUILD_DATE)'"
PKG_LIST := $(shell go list ./... | grep -v /vendor/)
DB_URL := postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable
DOCKER_PLATFORM := amd64
export PGPASSWORD := postgres
.PHONY: \
miniflux \
miniflux-no-pie \
linux-amd64 \
linux-arm64 \
linux-armv7 \
@ -43,7 +44,10 @@ export PGPASSWORD := postgres
debian-packages
miniflux:
@ CGO_ENABLED=0 go build -buildmode=pie -ldflags=$(LD_FLAGS) -o $(APP) main.go
@ go build -buildmode=pie -ldflags=$(LD_FLAGS) -o $(APP) main.go
miniflux-no-pie:
@ go build -ldflags=$(LD_FLAGS) -o $(APP) main.go
linux-amd64:
@ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
@ -73,7 +77,7 @@ openbsd-amd64:
@ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
windows-amd64:
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@.exe main.go
build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 darwin-arm64 freebsd-amd64 openbsd-amd64 windows-amd64
@ -94,19 +98,21 @@ openbsd-x86:
@ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
windows-x86:
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@.exe main.go
run:
@ LOG_DATE_TIME=1 DEBUG=1 RUN_MIGRATIONS=1 CREATE_ADMIN=1 ADMIN_USERNAME=admin ADMIN_PASSWORD=test123 go run main.go
@ LOG_DATE_TIME=1 LOG_LEVEL=debug RUN_MIGRATIONS=1 CREATE_ADMIN=1 ADMIN_USERNAME=admin ADMIN_PASSWORD=test123 go run main.go
clean:
@ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb
@ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb $(APP)*.exe
test:
go test -cover -race -count=1 ./...
lint:
golint -set_exit_status ${PKG_LIST}
go vet ./...
staticcheck ./...
golangci-lint run --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace
integration-test:
psql -U postgres -c 'drop database if exists miniflux_test;'
@ -120,9 +126,13 @@ integration-test:
RUN_MIGRATIONS=1 \
DEBUG=1 \
./miniflux-test >/tmp/miniflux.log 2>&1 & echo "$$!" > "/tmp/miniflux.pid"
while ! nc -z localhost 8080; do sleep 1; done
go test -v -tags=integration -count=1 miniflux.app/v2/internal/tests
TEST_MINIFLUX_BASE_URL=http://127.0.0.1:8080 \
TEST_MINIFLUX_ADMIN_USERNAME=admin \
TEST_MINIFLUX_ADMIN_PASSWORD=test123 \
go test -v -count=1 ./internal/api
clean-integration-test:
@ kill -9 `cat /tmp/miniflux.pid`
@ -131,7 +141,7 @@ clean-integration-test:
@ psql -U postgres -c 'drop database if exists miniflux_test;'
docker-image:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/alpine/Dockerfile .
docker build --pull -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/alpine/Dockerfile .
docker-image-distroless:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/distroless/Dockerfile .
@ -153,15 +163,15 @@ rpm: clean
rpmbuild -bb --define "_miniflux_version $(VERSION)" /root/rpmbuild/SPECS/miniflux.spec
debian:
@ docker build --load \
--build-arg BASE_IMAGE_ARCH=$(DEB_IMG_ARCH) \
-t $(DEB_IMG_ARCH)/miniflux-deb-builder \
@ docker buildx build --load \
--platform linux/$(DOCKER_PLATFORM) \
-t miniflux-deb-builder \
-f packaging/debian/Dockerfile \
.
@ docker run --rm \
-v ${PWD}:/pkg $(DEB_IMG_ARCH)/miniflux-deb-builder
@ docker run --rm --platform linux/$(DOCKER_PLATFORM) \
-v ${PWD}:/pkg miniflux-deb-builder
debian-packages: clean
$(MAKE) debian DEB_IMG_ARCH=amd64
$(MAKE) debian DEB_IMG_ARCH=arm64v8
$(MAKE) debian DEB_IMG_ARCH=arm32v7
$(MAKE) debian DOCKER_PLATFORM=amd64
$(MAKE) debian DOCKER_PLATFORM=arm64
$(MAKE) debian DOCKER_PLATFORM=arm/v7

View File

@ -18,16 +18,44 @@ type Client struct {
}
// New returns a new Miniflux client.
// Deprecated: use NewClient instead.
func New(endpoint string, credentials ...string) *Client {
// Web gives "API Endpoint = https://miniflux.app/v1/", it doesn't work (/v1/v1/me)
return NewClient(endpoint, credentials...)
}
// NewClient returns a new Miniflux client.
func NewClient(endpoint string, credentials ...string) *Client {
// Trim trailing slashes and /v1 from the endpoint.
endpoint = strings.TrimSuffix(endpoint, "/")
endpoint = strings.TrimSuffix(endpoint, "/v1")
// trim to https://miniflux.app
if len(credentials) == 2 {
switch len(credentials) {
case 2:
return &Client{request: &request{endpoint: endpoint, username: credentials[0], password: credentials[1]}}
case 1:
return &Client{request: &request{endpoint: endpoint, apiKey: credentials[0]}}
default:
return &Client{request: &request{endpoint: endpoint}}
}
return &Client{request: &request{endpoint: endpoint, apiKey: credentials[0]}}
}
// Healthcheck checks if the application is up and running.
func (c *Client) Healthcheck() error {
body, err := c.request.Get("/healthcheck")
if err != nil {
return fmt.Errorf("miniflux: unable to perform healthcheck: %w", err)
}
defer body.Close()
responseBodyContent, err := io.ReadAll(body)
if err != nil {
return fmt.Errorf("miniflux: unable to read healthcheck response: %w", err)
}
if string(responseBodyContent) != "OK" {
return fmt.Errorf("miniflux: invalid healthcheck response: %q", responseBodyContent)
}
return nil
}
// Version returns the version of the Miniflux instance.
@ -528,6 +556,25 @@ func (c *Client) SaveEntry(entryID int64) error {
return err
}
// FetchEntryOriginalContent fetches the original content of an entry using the scraper.
func (c *Client) FetchEntryOriginalContent(entryID int64) (string, error) {
body, err := c.request.Get(fmt.Sprintf("/v1/entries/%d/fetch-content", entryID))
if err != nil {
return "", err
}
defer body.Close()
var response struct {
Content string `json:"content"`
}
if err := json.NewDecoder(body).Decode(&response); err != nil {
return "", fmt.Errorf("miniflux: response error (%v)", err)
}
return response.Content, nil
}
// FetchCounters fetches feed counters.
func (c *Client) FetchCounters() (*FeedCounters, error) {
body, err := c.request.Get("/v1/feeds/counters")

View File

@ -12,7 +12,7 @@ This code snippet fetch the list of users:
miniflux "miniflux.app/v2/client"
)
client := miniflux.New("https://api.example.org", "admin", "secret")
client := miniflux.NewClient("https://api.example.org", "admin", "secret")
users, err := client.Users()
if err != nil {
fmt.Println(err)

View File

@ -41,6 +41,7 @@ type User struct {
DefaultHomePage string `json:"default_home_page"`
CategoriesSortingOrder string `json:"categories_sorting_order"`
MarkReadOnView bool `json:"mark_read_on_view"`
MediaPlaybackRate float64 `json:"media_playback_rate"`
}
func (u User) String() string {
@ -58,28 +59,29 @@ type UserCreationRequest struct {
// UserModificationRequest represents the request to update a user.
type UserModificationRequest struct {
Username *string `json:"username"`
Password *string `json:"password"`
IsAdmin *bool `json:"is_admin"`
Theme *string `json:"theme"`
Language *string `json:"language"`
Timezone *string `json:"timezone"`
EntryDirection *string `json:"entry_sorting_direction"`
EntryOrder *string `json:"entry_sorting_order"`
Stylesheet *string `json:"stylesheet"`
GoogleID *string `json:"google_id"`
OpenIDConnectID *string `json:"openid_connect_id"`
EntriesPerPage *int `json:"entries_per_page"`
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
ShowReadingTime *bool `json:"show_reading_time"`
EntrySwipe *bool `json:"entry_swipe"`
GestureNav *string `json:"gesture_nav"`
DisplayMode *string `json:"display_mode"`
DefaultReadingSpeed *int `json:"default_reading_speed"`
CJKReadingSpeed *int `json:"cjk_reading_speed"`
DefaultHomePage *string `json:"default_home_page"`
CategoriesSortingOrder *string `json:"categories_sorting_order"`
MarkReadOnView *bool `json:"mark_read_on_view"`
Username *string `json:"username"`
Password *string `json:"password"`
IsAdmin *bool `json:"is_admin"`
Theme *string `json:"theme"`
Language *string `json:"language"`
Timezone *string `json:"timezone"`
EntryDirection *string `json:"entry_sorting_direction"`
EntryOrder *string `json:"entry_sorting_order"`
Stylesheet *string `json:"stylesheet"`
GoogleID *string `json:"google_id"`
OpenIDConnectID *string `json:"openid_connect_id"`
EntriesPerPage *int `json:"entries_per_page"`
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
ShowReadingTime *bool `json:"show_reading_time"`
EntrySwipe *bool `json:"entry_swipe"`
GestureNav *string `json:"gesture_nav"`
DisplayMode *string `json:"display_mode"`
DefaultReadingSpeed *int `json:"default_reading_speed"`
CJKReadingSpeed *int `json:"cjk_reading_speed"`
DefaultHomePage *string `json:"default_home_page"`
CategoriesSortingOrder *string `json:"categories_sorting_order"`
MarkReadOnView *bool `json:"mark_read_on_view"`
MediaPlaybackRate *float64 `json:"media_playback_rate"`
}
// Users represents a list of users.
@ -107,7 +109,7 @@ type Subscription struct {
}
func (s Subscription) String() string {
return fmt.Sprintf(`Title="%s", URL="%s", Type="%s"`, s.Title, s.URL, s.Type)
return fmt.Sprintf(`Title=%q, URL=%q, Type=%q`, s.Title, s.URL, s.Type)
}
// Subscriptions represents a list of subscriptions.
@ -140,6 +142,7 @@ type Feed struct {
Password string `json:"password"`
Category *Category `json:"category,omitempty"`
HideGlobally bool `json:"hide_globally"`
DisableHTTP2 bool `json:"disable_http2"`
}
// FeedCreationRequest represents the request to create a feed.
@ -160,6 +163,7 @@ type FeedCreationRequest struct {
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"`
DisableHTTP2 bool `json:"disable_http2"`
}
// FeedModificationRequest represents the request to update a feed.
@ -182,6 +186,7 @@ type FeedModificationRequest struct {
AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"`
FetchViaProxy *bool `json:"fetch_via_proxy"`
HideGlobally *bool `json:"hide_globally"`
DisableHTTP2 *bool `json:"disable_http2"`
}
// FeedIcon represents the feed icon.
@ -202,24 +207,24 @@ type Feeds []*Feed
// Entry represents a subscription item in the system.
type Entry struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedID int64 `json:"feed_id"`
Status string `json:"status"`
Date time.Time `json:"published_at"`
ChangedAt time.Time `json:"changed_at"`
CreatedAt time.Time `json:"created_at"`
Feed *Feed `json:"feed,omitempty"`
Hash string `json:"hash"`
Title string `json:"title"`
URL string `json:"url"`
CommentsURL string `json:"comments_url"`
Date time.Time `json:"published_at"`
CreatedAt time.Time `json:"created_at"`
ChangedAt time.Time `json:"changed_at"`
Title string `json:"title"`
Status string `json:"status"`
Content string `json:"content"`
Author string `json:"author"`
ShareCode string `json:"share_code"`
Starred bool `json:"starred"`
ReadingTime int `json:"reading_time"`
Enclosures Enclosures `json:"enclosures,omitempty"`
Feed *Feed `json:"feed,omitempty"`
Tags []string `json:"tags"`
ReadingTime int `json:"reading_time"`
UserID int64 `json:"user_id"`
FeedID int64 `json:"feed_id"`
Starred bool `json:"starred"`
}
// EntryModificationRequest represents a request to modify an entry.
@ -287,3 +292,7 @@ type VersionResponse struct {
Arch string `json:"arch"`
OS string `json:"os"`
}
func SetOptionalField[T any](value T) *T {
return &value
}

View File

@ -26,6 +26,7 @@ var (
ErrForbidden = errors.New("miniflux: access forbidden")
ErrServerError = errors.New("miniflux: internal server error")
ErrNotFound = errors.New("miniflux: resource not found")
ErrBadRequest = errors.New("miniflux: bad request")
)
type errorResponse struct {
@ -124,10 +125,10 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser,
var resp errorResponse
decoder := json.NewDecoder(response.Body)
if err := decoder.Decode(&resp); err != nil {
return nil, fmt.Errorf("miniflux: bad request error (%v)", err)
return nil, fmt.Errorf("%w (%v)", ErrBadRequest, err)
}
return nil, fmt.Errorf("miniflux: bad request (%s)", resp.ErrorMessage)
return nil, fmt.Errorf("%w (%s)", ErrBadRequest, resp.ErrorMessage)
}
if response.StatusCode > 400 {

View File

@ -24,6 +24,7 @@ services:
environment:
- POSTGRES_USER=miniflux
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=miniflux
volumes:
- miniflux-db:/var/lib/postgresql/data
healthcheck:

63
go.mod
View File

@ -1,40 +1,47 @@
module miniflux.app/v2
// +heroku goVersion go1.21
// +heroku goVersion go1.22
require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/PuerkitoBio/goquery v1.9.2
github.com/abadojack/whatlanggo v1.0.1
github.com/coreos/go-oidc/v3 v3.6.0
github.com/gorilla/mux v1.8.0
github.com/andybalholm/brotli v1.1.0
github.com/coreos/go-oidc/v3 v3.10.0
github.com/go-webauthn/webauthn v0.10.2
github.com/gorilla/mux v1.8.1
github.com/lib/pq v1.10.9
github.com/mccutchen/go-httpbin/v2 v2.11.1
github.com/prometheus/client_golang v1.17.0
github.com/tdewolff/minify/v2 v2.12.9
github.com/yuin/goldmark v1.5.6
golang.org/x/crypto v0.14.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0
golang.org/x/term v0.13.0
github.com/prometheus/client_golang v1.19.1
github.com/tdewolff/minify/v2 v2.20.32
github.com/yuin/goldmark v1.7.1
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.20.0
golang.org/x/term v0.20.0
golang.org/x/text v0.15.0
mvdan.cc/xurls/v2 v2.5.0
)
require (
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/tdewolff/parse/v2 v2.6.8 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
github.com/go-webauthn/x v0.1.9 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/go-tpm v0.9.0 // indirect
)
go 1.21
require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/tdewolff/parse/v2 v2.7.14 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/sys v0.20.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
go 1.22

146
go.sum
View File

@ -1,116 +1,112 @@
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4=
github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU=
github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-webauthn/webauthn v0.10.2 h1:OG7B+DyuTytrEPFmTX503K77fqs3HDK/0Iv+z8UYbq4=
github.com/go-webauthn/webauthn v0.10.2/go.mod h1:Gd1IDsGAybuvK1NkwUTLbGmeksxuRJjVN2PE/xsPxHs=
github.com/go-webauthn/x v0.1.9 h1:v1oeLmoaa+gPOaZqUdDentu6Rl7HkSSsmOT6gxEQHhE=
github.com/go-webauthn/x v0.1.9/go.mod h1:pJNMlIMP1SU7cN8HNlKJpLEnFHCygLCvaLZ8a1xeoQA=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mccutchen/go-httpbin/v2 v2.11.1 h1:itBs6fEQvMKcGKIbMI9xzoGPZV56o2EMHA2rkCIdmLw=
github.com/mccutchen/go-httpbin/v2 v2.11.1/go.mod h1:f4DUXYlU6yH0V81O4lJIwqpmYdTXXmYwzxMnYEimFPk=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA=
github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU=
github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA=
github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tdewolff/minify/v2 v2.20.32 h1:rk4THvBPLEU+gGDKaJxyvFhF5+quSwCk3HKv1GpSVyE=
github.com/tdewolff/minify/v2 v2.20.32/go.mod h1:1TJni7+mATKu24cBQQpgwakrYRD27uC1/rdJOgdv8ns=
github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8=
github.com/tdewolff/parse/v2 v2.7.14/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA=
github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,11 @@ package api // import "miniflux.app/v2/internal/api"
import (
json_parser "encoding/json"
"log/slog"
"net/http"
"time"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/model"
@ -135,15 +137,27 @@ func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) {
userID := request.UserID(r)
categoryID := request.RouteInt64Param(r, "categoryID")
jobs, err := h.store.NewCategoryBatch(userID, categoryID, h.store.CountFeeds(userID))
batchBuilder := h.store.NewBatchBuilder()
batchBuilder.WithErrorLimit(config.Opts.PollingParsingErrorLimit())
batchBuilder.WithoutDisabledFeeds()
batchBuilder.WithUserID(userID)
batchBuilder.WithCategoryID(categoryID)
batchBuilder.WithNextCheckExpired()
jobs, err := batchBuilder.FetchJobs()
if err != nil {
json.ServerError(w, r, err)
return
}
go func() {
h.pool.Push(jobs)
}()
slog.Info(
"Triggered a manual refresh of all feeds for a given category from the API",
slog.Int64("user_id", userID),
slog.Int64("category_id", categoryID),
slog.Int("nb_jobs", len(jobs)),
)
go h.pool.Push(jobs)
json.NoContent(w, r)
}

View File

@ -15,8 +15,8 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/integration"
"miniflux.app/v2/internal/mediaproxy"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/proxy"
"miniflux.app/v2/internal/reader/processor"
"miniflux.app/v2/internal/reader/readingtime"
"miniflux.app/v2/internal/storage"
@ -36,14 +36,14 @@ func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b
return
}
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
proxyOption := config.Opts.ProxyOption()
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content)
proxyOption := config.Opts.MediaProxyMode()
for i := range entry.Enclosures {
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
for _, mediaType := range config.Opts.ProxyMediaTypes() {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, r.Host, entry.Enclosures[i].URL)
break
}
}
@ -164,7 +164,7 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
}
for i := range entries {
entries[i].Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entries[i].Content)
entries[i].Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entries[i].Content)
}
json.OK(w, r, &entriesResponse{Total: count, Entries: entries})
@ -275,7 +275,9 @@ func (h *handler) updateEntry(w http.ResponseWriter, r *http.Request) {
}
entryUpdateRequest.Patch(entry)
entry.ReadingTime = readingtime.EstimateReadingTime(entry.Content, user.DefaultReadingSpeed, user.CJKReadingSpeed)
if user.ShowReadingTime {
entry.ReadingTime = readingtime.EstimateReadingTime(entry.Content, user.DefaultReadingSpeed, user.CJKReadingSpeed)
}
if err := h.store.UpdateEntryTitleAndContent(entry); err != nil {
json.ServerError(w, r, err)

View File

@ -5,9 +5,11 @@ package api // import "miniflux.app/v2/internal/api"
import (
json_parser "encoding/json"
"log/slog"
"net/http"
"time"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/model"
@ -39,9 +41,9 @@ func (h *handler) createFeed(w http.ResponseWriter, r *http.Request) {
return
}
feed, err := feedHandler.CreateFeed(h.store, userID, &feedCreationRequest)
if err != nil {
json.ServerError(w, r, err)
feed, localizedError := feedHandler.CreateFeed(h.store, userID, &feedCreationRequest)
if localizedError != nil {
json.ServerError(w, r, localizedError.Error())
return
}
@ -57,9 +59,9 @@ func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
return
}
err := feedHandler.RefreshFeed(h.store, userID, feedID, false)
if err != nil {
json.ServerError(w, r, err)
localizedError := feedHandler.RefreshFeed(h.store, userID, feedID, false)
if localizedError != nil {
json.ServerError(w, r, localizedError.Error())
return
}
@ -68,15 +70,26 @@ func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
userID := request.UserID(r)
jobs, err := h.store.NewUserBatch(userID, h.store.CountFeeds(userID))
batchBuilder := h.store.NewBatchBuilder()
batchBuilder.WithErrorLimit(config.Opts.PollingParsingErrorLimit())
batchBuilder.WithoutDisabledFeeds()
batchBuilder.WithNextCheckExpired()
batchBuilder.WithUserID(userID)
jobs, err := batchBuilder.FetchJobs()
if err != nil {
json.ServerError(w, r, err)
return
}
go func() {
h.pool.Push(jobs)
}()
slog.Info(
"Triggered a manual refresh of all feeds from the API",
slog.Int64("user_id", userID),
slog.Int("nb_jobs", len(jobs)),
)
go h.pool.Push(jobs)
json.NoContent(w, r)
}
@ -102,7 +115,7 @@ func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
return
}
if validationErr := validator.ValidateFeedModification(h.store, userID, &feedModificationRequest); validationErr != nil {
if validationErr := validator.ValidateFeedModification(h.store, userID, originalFeed.ID, &feedModificationRequest); validationErr != nil {
json.BadRequest(w, r, validationErr.Error())
return
}

View File

@ -7,8 +7,11 @@ import (
json_parser "encoding/json"
"net/http"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/reader/fetcher"
"miniflux.app/v2/internal/reader/subscription"
"miniflux.app/v2/internal/validator"
)
@ -25,21 +28,33 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
return
}
subscriptions, finderErr := subscription.FindSubscriptions(
var rssbridgeURL string
intg, err := h.store.Integration(request.UserID(r))
if err == nil && intg != nil && intg.RSSBridgeEnabled {
rssbridgeURL = intg.RSSBridgeURL
}
requestBuilder := fetcher.NewRequestBuilder()
requestBuilder.WithTimeout(config.Opts.HTTPClientTimeout())
requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.WithUserAgent(subscriptionDiscoveryRequest.UserAgent, config.Opts.HTTPClientUserAgent())
requestBuilder.WithCookie(subscriptionDiscoveryRequest.Cookie)
requestBuilder.WithUsernameAndPassword(subscriptionDiscoveryRequest.Username, subscriptionDiscoveryRequest.Password)
requestBuilder.UseProxy(subscriptionDiscoveryRequest.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(subscriptionDiscoveryRequest.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(subscriptionDiscoveryRequest.DisableHTTP2)
subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions(
subscriptionDiscoveryRequest.URL,
subscriptionDiscoveryRequest.UserAgent,
subscriptionDiscoveryRequest.Cookie,
subscriptionDiscoveryRequest.Username,
subscriptionDiscoveryRequest.Password,
subscriptionDiscoveryRequest.FetchViaProxy,
subscriptionDiscoveryRequest.AllowSelfSignedCertificates,
rssbridgeURL,
)
if finderErr != nil {
json.ServerError(w, r, finderErr)
if localizedError != nil {
json.ServerError(w, r, localizedError.Error())
return
}
if subscriptions == nil {
if len(subscriptions) == 0 {
json.NotFound(w, r)
return
}

View File

@ -77,7 +77,7 @@ func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
}
if userModificationRequest.IsAdmin != nil && *userModificationRequest.IsAdmin {
json.BadRequest(w, r, errors.New("Only administrators can change permissions of standard users"))
json.BadRequest(w, r, errors.New("only administrators can change permissions of standard users"))
return
}
}
@ -141,7 +141,7 @@ func (h *handler) userByID(w http.ResponseWriter, r *http.Request) {
userID := request.RouteInt64Param(r, "userID")
user, err := h.store.UserByID(userID)
if err != nil {
json.BadRequest(w, r, errors.New("Unable to fetch this user from the database"))
json.BadRequest(w, r, errors.New("unable to fetch this user from the database"))
return
}
@ -163,7 +163,7 @@ func (h *handler) userByUsername(w http.ResponseWriter, r *http.Request) {
username := request.RouteStringParam(r, "username")
user, err := h.store.UserByUsername(username)
if err != nil {
json.BadRequest(w, r, errors.New("Unable to fetch this user from the database"))
json.BadRequest(w, r, errors.New("unable to fetch this user from the database"))
return
}
@ -194,7 +194,7 @@ func (h *handler) removeUser(w http.ResponseWriter, r *http.Request) {
}
if user.ID == request.UserID(r) {
json.BadRequest(w, r, errors.New("You cannot remove yourself"))
json.BadRequest(w, r, errors.New("you cannot remove yourself"))
return
}

View File

@ -16,7 +16,7 @@ func askCredentials() (string, string) {
fd := int(os.Stdin.Fd())
if !term.IsTerminal(fd) {
printErrorAndExit(fmt.Errorf("this is not a terminal, exiting"))
printErrorAndExit(fmt.Errorf("this is not an interactive terminal, exiting"))
}
fmt.Print("Enter Username: ")

View File

@ -23,7 +23,7 @@ const (
flagVersionHelp = "Show application version"
flagMigrateHelp = "Run SQL migrations"
flagFlushSessionsHelp = "Flush all sessions (disconnect users)"
flagCreateAdminHelp = "Create admin user"
flagCreateAdminHelp = "Create an admin user from an interactive terminal"
flagResetPasswordHelp = "Reset user password"
flagResetFeedErrorsHelp = "Clear all feed errors for all users"
flagDebugModeHelp = "Show debug logs"
@ -191,7 +191,7 @@ func Parse() {
}
if flagCreateAdmin {
createAdmin(store)
createAdminUserFromInteractiveTerminal(store)
return
}
@ -211,9 +211,8 @@ func Parse() {
printErrorAndExit(err)
}
// Create admin user and start the daemon.
if config.Opts.CreateAdmin() {
createAdmin(store)
createAdminUserFromEnvironmentVariables(store)
}
if flagRefreshFeeds {

View File

@ -12,15 +12,20 @@ import (
"miniflux.app/v2/internal/validator"
)
func createAdmin(store *storage.Storage) {
userCreationRequest := &model.UserCreationRequest{
Username: config.Opts.AdminUsername(),
Password: config.Opts.AdminPassword(),
IsAdmin: true,
}
func createAdminUserFromEnvironmentVariables(store *storage.Storage) {
createAdminUser(store, config.Opts.AdminUsername(), config.Opts.AdminPassword())
}
if userCreationRequest.Username == "" || userCreationRequest.Password == "" {
userCreationRequest.Username, userCreationRequest.Password = askCredentials()
func createAdminUserFromInteractiveTerminal(store *storage.Storage) {
username, password := askCredentials()
createAdminUser(store, username, password)
}
func createAdminUser(store *storage.Storage, username, password string) {
userCreationRequest := &model.UserCreationRequest{
Username: username,
Password: password,
IsAdmin: true,
}
if store.UserExists(userCreationRequest.Username) {
@ -34,7 +39,12 @@ func createAdmin(store *storage.Storage) {
printErrorAndExit(validationErr.Error())
}
if _, err := store.CreateUser(userCreationRequest); err != nil {
if user, err := store.CreateUser(userCreationRequest); err != nil {
printErrorAndExit(err)
} else {
slog.Info("Created new admin user",
slog.String("username", user.Username),
slog.Int64("user_id", user.ID),
)
}
}

View File

@ -18,9 +18,18 @@ func refreshFeeds(store *storage.Storage) {
var wg sync.WaitGroup
startTime := time.Now()
jobs, err := store.NewBatch(config.Opts.BatchSize())
// Generate a batch of feeds for any user that has feeds to refresh.
batchBuilder := store.NewBatchBuilder()
batchBuilder.WithBatchSize(config.Opts.BatchSize())
batchBuilder.WithErrorLimit(config.Opts.PollingParsingErrorLimit())
batchBuilder.WithoutDisabledFeeds()
batchBuilder.WithNextCheckExpired()
jobs, err := batchBuilder.FetchJobs()
if err != nil {
slog.Error("Unable to fetch jobs from database", slog.Any("error", err))
return
}
nbJobs := len(jobs)
@ -36,7 +45,7 @@ func refreshFeeds(store *storage.Storage) {
slog.Int("nb_workers", config.Opts.WorkerPoolSize()),
)
for i := 0; i < config.Opts.WorkerPoolSize(); i++ {
for i := range config.Opts.WorkerPoolSize() {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
@ -47,11 +56,11 @@ func refreshFeeds(store *storage.Storage) {
slog.Int("worker_id", workerID),
)
if err := feedHandler.RefreshFeed(store, job.UserID, job.FeedID, false); err != nil {
slog.Error("Unable to refresh feed",
if localizedError := feedHandler.RefreshFeed(store, job.UserID, job.FeedID, false); localizedError != nil {
slog.Warn("Unable to refresh feed",
slog.Int64("feed_id", job.FeedID),
slog.Int64("user_id", job.UserID),
slog.Any("error", err),
slog.Any("error", localizedError.Error()),
)
}
}

View File

@ -20,6 +20,7 @@ func runScheduler(store *storage.Storage, pool *worker.Pool) {
pool,
config.Opts.PollingFrequency(),
config.Opts.BatchSize(),
config.Opts.PollingParsingErrorLimit(),
)
go cleanupScheduler(
@ -28,12 +29,18 @@ func runScheduler(store *storage.Storage, pool *worker.Pool) {
)
}
func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize int) {
func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize, errorLimit int) {
for range time.Tick(time.Duration(frequency) * time.Minute) {
jobs, err := store.NewBatch(batchSize)
if err != nil {
// Generate a batch of feeds for any user that has feeds to refresh.
batchBuilder := store.NewBatchBuilder()
batchBuilder.WithBatchSize(batchSize)
batchBuilder.WithErrorLimit(errorLimit)
batchBuilder.WithoutDisabledFeeds()
batchBuilder.WithNextCheckExpired()
if jobs, err := batchBuilder.FetchJobs(); err != nil {
slog.Error("Unable to fetch jobs from database", slog.Any("error", err))
} else {
} else if len(jobs) > 0 {
slog.Info("Created a batch of feeds",
slog.Int("nb_jobs", len(jobs)),
)

View File

@ -4,6 +4,7 @@
package config // import "miniflux.app/v2/internal/config"
import (
"bytes"
"os"
"testing"
)
@ -759,6 +760,41 @@ func TestPollingFrequency(t *testing.T) {
}
}
func TestDefautForceRefreshInterval(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := defaultForceRefreshInterval
result := opts.ForceRefreshInterval()
if result != expected {
t.Fatalf(`Unexpected FORCE_REFRESH_INTERVAL value, got %v instead of %v`, result, expected)
}
}
func TestForceRefreshInterval(t *testing.T) {
os.Clearenv()
os.Setenv("FORCE_REFRESH_INTERVAL", "42")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := 42
result := opts.ForceRefreshInterval()
if result != expected {
t.Fatalf(`Unexpected FORCE_REFRESH_INTERVAL value, got %v instead of %v`, result, expected)
}
}
func TestDefaultBatchSizeValue(t *testing.T) {
os.Clearenv()
@ -829,7 +865,7 @@ func TestPollingScheduler(t *testing.T) {
}
}
func TestDefautSchedulerCountBasedMaxIntervalValue(t *testing.T) {
func TestDefautSchedulerEntryFrequencyMaxIntervalValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
@ -846,7 +882,7 @@ func TestDefautSchedulerCountBasedMaxIntervalValue(t *testing.T) {
}
}
func TestDefautSchedulerCountBasedMaxInterval(t *testing.T) {
func TestSchedulerEntryFrequencyMaxInterval(t *testing.T) {
os.Clearenv()
os.Setenv("SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL", "30")
@ -864,7 +900,7 @@ func TestDefautSchedulerCountBasedMaxInterval(t *testing.T) {
}
}
func TestDefautSchedulerCountBasedMinIntervalValue(t *testing.T) {
func TestDefautSchedulerEntryFrequencyMinIntervalValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
@ -881,7 +917,7 @@ func TestDefautSchedulerCountBasedMinIntervalValue(t *testing.T) {
}
}
func TestDefautSchedulerCountBasedMinInterval(t *testing.T) {
func TestSchedulerEntryFrequencyMinInterval(t *testing.T) {
os.Clearenv()
os.Setenv("SCHEDULER_ENTRY_FREQUENCY_MIN_INTERVAL", "30")
@ -916,7 +952,7 @@ func TestDefautSchedulerEntryFrequencyFactorValue(t *testing.T) {
}
}
func TestDefautSchedulerEntryFrequencyFactor(t *testing.T) {
func TestSchedulerEntryFrequencyFactor(t *testing.T) {
os.Clearenv()
os.Setenv("SCHEDULER_ENTRY_FREQUENCY_FACTOR", "2")
@ -934,6 +970,41 @@ func TestDefautSchedulerEntryFrequencyFactor(t *testing.T) {
}
}
func TestDefaultSchedulerRoundRobinValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := defaultSchedulerRoundRobinMinInterval
result := opts.SchedulerRoundRobinMinInterval()
if result != expected {
t.Fatalf(`Unexpected SCHEDULER_ROUND_ROBIN_MIN_INTERVAL value, got %v instead of %v`, result, expected)
}
}
func TestSchedulerRoundRobin(t *testing.T) {
os.Clearenv()
os.Setenv("SCHEDULER_ROUND_ROBIN_MIN_INTERVAL", "15")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := 15
result := opts.SchedulerRoundRobinMinInterval()
if result != expected {
t.Fatalf(`Unexpected SCHEDULER_ROUND_ROBIN_MIN_INTERVAL value, got %v instead of %v`, result, expected)
}
}
func TestPollingParsingErrorLimit(t *testing.T) {
os.Clearenv()
os.Setenv("POLLING_PARSING_ERROR_LIMIT", "100")
@ -1372,9 +1443,9 @@ func TestPocketConsumerKeyFromUserPrefs(t *testing.T) {
}
}
func TestProxyOption(t *testing.T) {
func TestMediaProxyMode(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_OPTION", "all")
os.Setenv("MEDIA_PROXY_MODE", "all")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
@ -1383,14 +1454,14 @@ func TestProxyOption(t *testing.T) {
}
expected := "all"
result := opts.ProxyOption()
result := opts.MediaProxyMode()
if result != expected {
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
t.Fatalf(`Unexpected MEDIA_PROXY_MODE value, got %q instead of %q`, result, expected)
}
}
func TestDefaultProxyOptionValue(t *testing.T) {
func TestDefaultMediaProxyModeValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
@ -1399,17 +1470,17 @@ func TestDefaultProxyOptionValue(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := defaultProxyOption
result := opts.ProxyOption()
expected := defaultMediaProxyMode
result := opts.MediaProxyMode()
if result != expected {
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
t.Fatalf(`Unexpected MEDIA_PROXY_MODE value, got %q instead of %q`, result, expected)
}
}
func TestProxyMediaTypes(t *testing.T) {
func TestMediaProxyResourceTypes(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_MEDIA_TYPES", "image,audio")
os.Setenv("MEDIA_PROXY_RESOURCE_TYPES", "image,audio")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
@ -1419,25 +1490,25 @@ func TestProxyMediaTypes(t *testing.T) {
expected := []string{"audio", "image"}
if len(expected) != len(opts.ProxyMediaTypes()) {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
if len(expected) != len(opts.MediaProxyResourceTypes()) {
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
resultMap := make(map[string]bool)
for _, mediaType := range opts.ProxyMediaTypes() {
for _, mediaType := range opts.MediaProxyResourceTypes() {
resultMap[mediaType] = true
}
for _, mediaType := range expected {
if !resultMap[mediaType] {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
}
}
func TestProxyMediaTypesWithDuplicatedValues(t *testing.T) {
func TestMediaProxyResourceTypesWithDuplicatedValues(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_MEDIA_TYPES", "image,audio, image")
os.Setenv("MEDIA_PROXY_RESOURCE_TYPES", "image,audio, image")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
@ -1446,23 +1517,119 @@ func TestProxyMediaTypesWithDuplicatedValues(t *testing.T) {
}
expected := []string{"audio", "image"}
if len(expected) != len(opts.ProxyMediaTypes()) {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
if len(expected) != len(opts.MediaProxyResourceTypes()) {
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
resultMap := make(map[string]bool)
for _, mediaType := range opts.ProxyMediaTypes() {
for _, mediaType := range opts.MediaProxyResourceTypes() {
resultMap[mediaType] = true
}
for _, mediaType := range expected {
if !resultMap[mediaType] {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
}
}
func TestProxyImagesOptionBackwardCompatibility(t *testing.T) {
func TestDefaultMediaProxyResourceTypes(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := []string{"image"}
if len(expected) != len(opts.MediaProxyResourceTypes()) {
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
resultMap := make(map[string]bool)
for _, mediaType := range opts.MediaProxyResourceTypes() {
resultMap[mediaType] = true
}
for _, mediaType := range expected {
if !resultMap[mediaType] {
t.Fatalf(`Unexpected MEDIA_PROXY_RESOURCE_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
}
}
func TestMediaProxyHTTPClientTimeout(t *testing.T) {
os.Clearenv()
os.Setenv("MEDIA_PROXY_HTTP_CLIENT_TIMEOUT", "24")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := 24
result := opts.MediaProxyHTTPClientTimeout()
if result != expected {
t.Fatalf(`Unexpected MEDIA_PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
}
}
func TestDefaultMediaProxyHTTPClientTimeoutValue(t *testing.T) {
os.Clearenv()
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := defaultMediaProxyHTTPClientTimeout
result := opts.MediaProxyHTTPClientTimeout()
if result != expected {
t.Fatalf(`Unexpected MEDIA_PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
}
}
func TestMediaProxyCustomURL(t *testing.T) {
os.Clearenv()
os.Setenv("MEDIA_PROXY_CUSTOM_URL", "http://example.org/proxy")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := "http://example.org/proxy"
result := opts.MediaCustomProxyURL()
if result != expected {
t.Fatalf(`Unexpected MEDIA_PROXY_CUSTOM_URL value, got %q instead of %q`, result, expected)
}
}
func TestMediaProxyPrivateKey(t *testing.T) {
os.Clearenv()
os.Setenv("MEDIA_PROXY_PRIVATE_KEY", "foobar")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := []byte("foobar")
result := opts.MediaProxyPrivateKey()
if !bytes.Equal(result, expected) {
t.Fatalf(`Unexpected MEDIA_PROXY_PRIVATE_KEY value, got %q instead of %q`, result, expected)
}
}
func TestProxyImagesOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_IMAGES", "all")
@ -1473,30 +1640,31 @@ func TestProxyImagesOptionBackwardCompatibility(t *testing.T) {
}
expected := []string{"image"}
if len(expected) != len(opts.ProxyMediaTypes()) {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
if len(expected) != len(opts.MediaProxyResourceTypes()) {
t.Fatalf(`Unexpected PROXY_IMAGES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
resultMap := make(map[string]bool)
for _, mediaType := range opts.ProxyMediaTypes() {
for _, mediaType := range opts.MediaProxyResourceTypes() {
resultMap[mediaType] = true
}
for _, mediaType := range expected {
if !resultMap[mediaType] {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
t.Fatalf(`Unexpected PROXY_IMAGES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
}
expectedProxyOption := "all"
result := opts.ProxyOption()
result := opts.MediaProxyMode()
if result != expectedProxyOption {
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expectedProxyOption)
}
}
func TestDefaultProxyMediaTypes(t *testing.T) {
func TestProxyImageURLForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_IMAGE_URL", "http://example.org/proxy")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
@ -1504,25 +1672,73 @@ func TestDefaultProxyMediaTypes(t *testing.T) {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := []string{"image"}
expected := "http://example.org/proxy"
result := opts.MediaCustomProxyURL()
if result != expected {
t.Fatalf(`Unexpected PROXY_IMAGE_URL value, got %q instead of %q`, result, expected)
}
}
if len(expected) != len(opts.ProxyMediaTypes()) {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
func TestProxyURLOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_URL", "http://example.org/proxy")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := "http://example.org/proxy"
result := opts.MediaCustomProxyURL()
if result != expected {
t.Fatalf(`Unexpected PROXY_URL value, got %q instead of %q`, result, expected)
}
}
func TestProxyMediaTypesOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_MEDIA_TYPES", "image,audio")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := []string{"audio", "image"}
if len(expected) != len(opts.MediaProxyResourceTypes()) {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
resultMap := make(map[string]bool)
for _, mediaType := range opts.ProxyMediaTypes() {
for _, mediaType := range opts.MediaProxyResourceTypes() {
resultMap[mediaType] = true
}
for _, mediaType := range expected {
if !resultMap[mediaType] {
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.MediaProxyResourceTypes(), expected)
}
}
}
func TestProxyHTTPClientTimeout(t *testing.T) {
func TestProxyOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_OPTION", "all")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := "all"
result := opts.MediaProxyMode()
if result != expected {
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
}
}
func TestProxyHTTPClientTimeoutOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_HTTP_CLIENT_TIMEOUT", "24")
@ -1531,29 +1747,26 @@ func TestProxyHTTPClientTimeout(t *testing.T) {
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := 24
result := opts.ProxyHTTPClientTimeout()
result := opts.MediaProxyHTTPClientTimeout()
if result != expected {
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
}
}
func TestDefaultProxyHTTPClientTimeoutValue(t *testing.T) {
func TestProxyPrivateKeyOptionForBackwardCompatibility(t *testing.T) {
os.Clearenv()
os.Setenv("PROXY_PRIVATE_KEY", "foobar")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := defaultProxyHTTPClientTimeout
result := opts.ProxyHTTPClientTimeout()
if result != expected {
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
expected := []byte("foobar")
result := opts.MediaProxyPrivateKey()
if !bytes.Equal(result, expected) {
t.Fatalf(`Unexpected PROXY_PRIVATE_KEY value, got %q instead of %q`, result, expected)
}
}
@ -1808,6 +2021,24 @@ func TestAuthProxyUserCreationAdmin(t *testing.T) {
}
}
func TestFetchNebulaWatchTime(t *testing.T) {
os.Clearenv()
os.Setenv("FETCH_NEBULA_WATCH_TIME", "1")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := true
result := opts.FetchNebulaWatchTime()
if result != expected {
t.Fatalf(`Unexpected FETCH_NEBULA_WATCH_TIME value, got %v instead of %v`, result, expected)
}
}
func TestFetchOdyseeWatchTime(t *testing.T) {
os.Clearenv()
os.Setenv("FETCH_ODYSEE_WATCH_TIME", "1")
@ -1874,7 +2105,7 @@ func TestParseConfigDumpOutput(t *testing.T) {
t.Fatal(err)
}
if _, err := tmpfile.Write([]byte(serialized)); err != nil {
if _, err := tmpfile.WriteString(serialized); err != nil {
t.Fatal(err)
}

View File

@ -4,12 +4,12 @@
package config // import "miniflux.app/v2/internal/config"
import (
"crypto/rand"
"fmt"
"sort"
"strings"
"time"
"miniflux.app/v2/internal/crypto"
"miniflux.app/v2/internal/version"
)
@ -27,13 +27,15 @@ const (
defaultBaseURL = "http://localhost"
defaultRootURL = "http://localhost"
defaultBasePath = ""
defaultWorkerPoolSize = 5
defaultWorkerPoolSize = 16
defaultPollingFrequency = 60
defaultForceRefreshInterval = 30
defaultBatchSize = 100
defaultPollingScheduler = "round_robin"
defaultSchedulerEntryFrequencyMinInterval = 5
defaultSchedulerEntryFrequencyMaxInterval = 24 * 60
defaultSchedulerEntryFrequencyFactor = 1
defaultSchedulerRoundRobinMinInterval = 60
defaultPollingParsingErrorLimit = 3
defaultRunMigrations = false
defaultDatabaseURL = "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
@ -49,10 +51,12 @@ const (
defaultCleanupArchiveUnreadDays = 180
defaultCleanupArchiveBatchSize = 10000
defaultCleanupRemoveSessionsDays = 30
defaultProxyHTTPClientTimeout = 120
defaultProxyOption = "http-only"
defaultProxyMediaTypes = "image"
defaultProxyUrl = ""
defaultMediaProxyHTTPClientTimeout = 120
defaultMediaProxyMode = "http-only"
defaultMediaResourceTypes = "image"
defaultMediaProxyURL = ""
defaultFilterEntryMaxAgeDays = 0
defaultFetchNebulaWatchTime = false
defaultFetchOdyseeWatchTime = false
defaultFetchYouTubeWatchTime = false
defaultYouTubeEmbedUrlOverride = "https://www.youtube-nocookie.com/embed/"
@ -81,6 +85,7 @@ const (
defaultMetricsPassword = ""
defaultWatchdog = true
defaultInvidiousInstance = "yewtu.be"
defaultWebAuthn = false
)
var defaultHTTPClientUserAgent = "Mozilla/5.0 (compatible; Miniflux/" + version.Version + "; +https://miniflux.app)"
@ -120,22 +125,26 @@ type Options struct {
cleanupArchiveBatchSize int
cleanupRemoveSessionsDays int
pollingFrequency int
forceRefreshInterval int
batchSize int
pollingScheduler string
schedulerEntryFrequencyMinInterval int
schedulerEntryFrequencyMaxInterval int
schedulerEntryFrequencyFactor int
schedulerRoundRobinMinInterval int
pollingParsingErrorLimit int
workerPoolSize int
createAdmin bool
adminUsername string
adminPassword string
proxyHTTPClientTimeout int
proxyOption string
proxyMediaTypes []string
proxyUrl string
mediaProxyHTTPClientTimeout int
mediaProxyMode string
mediaProxyResourceTypes []string
mediaProxyCustomURL string
fetchNebulaWatchTime bool
fetchOdyseeWatchTime bool
fetchYouTubeWatchTime bool
filterEntryMaxAgeDays int
youTubeEmbedUrlOverride string
oauth2UserCreationAllowed bool
oauth2ClientID string
@ -160,14 +169,12 @@ type Options struct {
metricsPassword string
watchdog bool
invidiousInstance string
proxyPrivateKey []byte
mediaProxyPrivateKey []byte
webAuthn bool
}
// NewOptions returns Options with default values.
func NewOptions() *Options {
randomKey := make([]byte, 16)
rand.Read(randomKey)
return &Options{
HTTPS: defaultHTTPS,
logFile: defaultLogFile,
@ -196,18 +203,22 @@ func NewOptions() *Options {
cleanupArchiveBatchSize: defaultCleanupArchiveBatchSize,
cleanupRemoveSessionsDays: defaultCleanupRemoveSessionsDays,
pollingFrequency: defaultPollingFrequency,
forceRefreshInterval: defaultForceRefreshInterval,
batchSize: defaultBatchSize,
pollingScheduler: defaultPollingScheduler,
schedulerEntryFrequencyMinInterval: defaultSchedulerEntryFrequencyMinInterval,
schedulerEntryFrequencyMaxInterval: defaultSchedulerEntryFrequencyMaxInterval,
schedulerEntryFrequencyFactor: defaultSchedulerEntryFrequencyFactor,
schedulerRoundRobinMinInterval: defaultSchedulerRoundRobinMinInterval,
pollingParsingErrorLimit: defaultPollingParsingErrorLimit,
workerPoolSize: defaultWorkerPoolSize,
createAdmin: defaultCreateAdmin,
proxyHTTPClientTimeout: defaultProxyHTTPClientTimeout,
proxyOption: defaultProxyOption,
proxyMediaTypes: []string{defaultProxyMediaTypes},
proxyUrl: defaultProxyUrl,
mediaProxyHTTPClientTimeout: defaultMediaProxyHTTPClientTimeout,
mediaProxyMode: defaultMediaProxyMode,
mediaProxyResourceTypes: []string{defaultMediaResourceTypes},
mediaProxyCustomURL: defaultMediaProxyURL,
filterEntryMaxAgeDays: defaultFilterEntryMaxAgeDays,
fetchNebulaWatchTime: defaultFetchNebulaWatchTime,
fetchOdyseeWatchTime: defaultFetchOdyseeWatchTime,
fetchYouTubeWatchTime: defaultFetchYouTubeWatchTime,
youTubeEmbedUrlOverride: defaultYouTubeEmbedUrlOverride,
@ -234,7 +245,8 @@ func NewOptions() *Options {
metricsPassword: defaultMetricsPassword,
watchdog: defaultWatchdog,
invidiousInstance: defaultInvidiousInstance,
proxyPrivateKey: randomKey,
mediaProxyPrivateKey: crypto.GenerateRandomBytes(16),
webAuthn: defaultWebAuthn,
}
}
@ -372,6 +384,11 @@ func (o *Options) PollingFrequency() int {
return o.pollingFrequency
}
// ForceRefreshInterval returns the force refresh interval
func (o *Options) ForceRefreshInterval() int {
return o.forceRefreshInterval
}
// BatchSize returns the number of feeds to send for background processing.
func (o *Options) BatchSize() int {
return o.batchSize
@ -397,6 +414,10 @@ func (o *Options) SchedulerEntryFrequencyFactor() int {
return o.schedulerEntryFrequencyFactor
}
func (o *Options) SchedulerRoundRobinMinInterval() int {
return o.schedulerRoundRobinMinInterval
}
// PollingParsingErrorLimit returns the limit of errors when to stop polling.
func (o *Options) PollingParsingErrorLimit() int {
return o.pollingParsingErrorLimit
@ -468,30 +489,41 @@ func (o *Options) YouTubeEmbedUrlOverride() string {
return o.youTubeEmbedUrlOverride
}
// FetchNebulaWatchTime returns true if the Nebula video duration
// should be fetched and used as a reading time.
func (o *Options) FetchNebulaWatchTime() bool {
return o.fetchNebulaWatchTime
}
// FetchOdyseeWatchTime returns true if the Odysee video duration
// should be fetched and used as a reading time.
func (o *Options) FetchOdyseeWatchTime() bool {
return o.fetchOdyseeWatchTime
}
// ProxyOption returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
func (o *Options) ProxyOption() string {
return o.proxyOption
// MediaProxyMode returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
func (o *Options) MediaProxyMode() string {
return o.mediaProxyMode
}
// ProxyMediaTypes returns a slice of media types to proxy.
func (o *Options) ProxyMediaTypes() []string {
return o.proxyMediaTypes
// MediaProxyResourceTypes returns a slice of resource types to proxy.
func (o *Options) MediaProxyResourceTypes() []string {
return o.mediaProxyResourceTypes
}
// ProxyUrl returns a string of a URL to use to proxy image requests
func (o *Options) ProxyUrl() string {
return o.proxyUrl
// MediaCustomProxyURL returns the custom proxy URL for medias.
func (o *Options) MediaCustomProxyURL() string {
return o.mediaProxyCustomURL
}
// ProxyHTTPClientTimeout returns the time limit in seconds before the proxy HTTP client cancel the request.
func (o *Options) ProxyHTTPClientTimeout() int {
return o.proxyHTTPClientTimeout
// MediaProxyHTTPClientTimeout returns the time limit in seconds before the proxy HTTP client cancel the request.
func (o *Options) MediaProxyHTTPClientTimeout() int {
return o.mediaProxyHTTPClientTimeout
}
// MediaProxyPrivateKey returns the private key used by the media proxy.
func (o *Options) MediaProxyPrivateKey() []byte {
return o.mediaProxyPrivateKey
}
// HasHTTPService returns true if the HTTP service is enabled.
@ -587,9 +619,14 @@ func (o *Options) InvidiousInstance() string {
return o.invidiousInstance
}
// ProxyPrivateKey returns the private key used by the media proxy
func (o *Options) ProxyPrivateKey() []byte {
return o.proxyPrivateKey
// WebAuthn returns true if WebAuthn logins are supported
func (o *Options) WebAuthn() bool {
return o.webAuthn
}
// FilterEntryMaxAgeDays returns the number of days after which entries should be retained.
func (o *Options) FilterEntryMaxAgeDays() int {
return o.filterEntryMaxAgeDays
}
// SortedOptions returns options as a list of key value pairs, sorted by keys.
@ -617,7 +654,9 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
"DISABLE_HSTS": !o.hsts,
"DISABLE_HTTP_SERVICE": !o.httpService,
"DISABLE_SCHEDULER_SERVICE": !o.schedulerService,
"FILTER_ENTRY_MAX_AGE_DAYS": o.filterEntryMaxAgeDays,
"FETCH_YOUTUBE_WATCH_TIME": o.fetchYouTubeWatchTime,
"FETCH_NEBULA_WATCH_TIME": o.fetchNebulaWatchTime,
"FETCH_ODYSEE_WATCH_TIME": o.fetchOdyseeWatchTime,
"HTTPS": o.HTTPS,
"HTTP_CLIENT_MAX_BODY_SIZE": o.httpClientMaxBodySize,
@ -648,23 +687,26 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
"OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed,
"POCKET_CONSUMER_KEY": redactSecretValue(o.pocketConsumerKey, redactSecret),
"POLLING_FREQUENCY": o.pollingFrequency,
"FORCE_REFRESH_INTERVAL": o.forceRefreshInterval,
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
"POLLING_SCHEDULER": o.pollingScheduler,
"PROXY_HTTP_CLIENT_TIMEOUT": o.proxyHTTPClientTimeout,
"PROXY_MEDIA_TYPES": o.proxyMediaTypes,
"PROXY_OPTION": o.proxyOption,
"PROXY_PRIVATE_KEY": redactSecretValue(string(o.proxyPrivateKey), redactSecret),
"PROXY_URL": o.proxyUrl,
"MEDIA_PROXY_HTTP_CLIENT_TIMEOUT": o.mediaProxyHTTPClientTimeout,
"MEDIA_PROXY_RESOURCE_TYPES": o.mediaProxyResourceTypes,
"MEDIA_PROXY_MODE": o.mediaProxyMode,
"MEDIA_PROXY_PRIVATE_KEY": redactSecretValue(string(o.mediaProxyPrivateKey), redactSecret),
"MEDIA_PROXY_CUSTOM_URL": o.mediaProxyCustomURL,
"ROOT_URL": o.rootURL,
"RUN_MIGRATIONS": o.runMigrations,
"SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": o.schedulerEntryFrequencyMaxInterval,
"SCHEDULER_ENTRY_FREQUENCY_MIN_INTERVAL": o.schedulerEntryFrequencyMinInterval,
"SCHEDULER_ENTRY_FREQUENCY_FACTOR": o.schedulerEntryFrequencyFactor,
"SCHEDULER_ROUND_ROBIN_MIN_INTERVAL": o.schedulerRoundRobinMinInterval,
"SCHEDULER_SERVICE": o.schedulerService,
"SERVER_TIMING_HEADER": o.serverTimingHeader,
"WATCHDOG": o.watchdog,
"WORKER_POOL_SIZE": o.workerPoolSize,
"YOUTUBE_EMBED_URL_OVERRIDE": o.youTubeEmbedUrlOverride,
"WEBAUTHN": o.webAuthn,
}
keys := make([]string, 0, len(keyValues))

View File

@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/url"
"os"
"strconv"
@ -56,7 +57,7 @@ func (p *Parser) parseFileContent(r io.Reader) (lines []string) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) > 0 && !strings.HasPrefix(line, "#") && strings.Index(line, "=") > 0 {
if !strings.HasPrefix(line, "#") && strings.Index(line, "=") > 0 {
lines = append(lines, line)
}
}
@ -87,6 +88,7 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.logFormat = parsedValue
}
case "DEBUG":
slog.Warn("The DEBUG environment variable is deprecated, use LOG_LEVEL instead")
parsedValue := parseBool(value, defaultDebug)
if parsedValue {
p.opts.logLevel = "debug"
@ -112,6 +114,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.databaseMinConns = parseInt(value, defaultDatabaseMinConns)
case "DATABASE_CONNECTION_LIFETIME":
p.opts.databaseConnectionLifetime = parseInt(value, defaultDatabaseConnectionLifetime)
case "FILTER_ENTRY_MAX_AGE_DAYS":
p.opts.filterEntryMaxAgeDays = parseInt(value, defaultFilterEntryMaxAgeDays)
case "RUN_MIGRATIONS":
p.opts.runMigrations = parseBool(value, defaultRunMigrations)
case "DISABLE_HSTS":
@ -142,6 +146,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.workerPoolSize = parseInt(value, defaultWorkerPoolSize)
case "POLLING_FREQUENCY":
p.opts.pollingFrequency = parseInt(value, defaultPollingFrequency)
case "FORCE_REFRESH_INTERVAL":
p.opts.forceRefreshInterval = parseInt(value, defaultForceRefreshInterval)
case "BATCH_SIZE":
p.opts.batchSize = parseInt(value, defaultBatchSize)
case "POLLING_SCHEDULER":
@ -152,22 +158,45 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.schedulerEntryFrequencyMinInterval = parseInt(value, defaultSchedulerEntryFrequencyMinInterval)
case "SCHEDULER_ENTRY_FREQUENCY_FACTOR":
p.opts.schedulerEntryFrequencyFactor = parseInt(value, defaultSchedulerEntryFrequencyFactor)
case "SCHEDULER_ROUND_ROBIN_MIN_INTERVAL":
p.opts.schedulerRoundRobinMinInterval = parseInt(value, defaultSchedulerRoundRobinMinInterval)
case "POLLING_PARSING_ERROR_LIMIT":
p.opts.pollingParsingErrorLimit = parseInt(value, defaultPollingParsingErrorLimit)
// kept for compatibility purpose
case "PROXY_IMAGES":
p.opts.proxyOption = parseString(value, defaultProxyOption)
slog.Warn("The PROXY_IMAGES environment variable is deprecated, use MEDIA_PROXY_MODE instead")
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
case "PROXY_HTTP_CLIENT_TIMEOUT":
p.opts.proxyHTTPClientTimeout = parseInt(value, defaultProxyHTTPClientTimeout)
slog.Warn("The PROXY_HTTP_CLIENT_TIMEOUT environment variable is deprecated, use MEDIA_PROXY_HTTP_CLIENT_TIMEOUT instead")
p.opts.mediaProxyHTTPClientTimeout = parseInt(value, defaultMediaProxyHTTPClientTimeout)
case "MEDIA_PROXY_HTTP_CLIENT_TIMEOUT":
p.opts.mediaProxyHTTPClientTimeout = parseInt(value, defaultMediaProxyHTTPClientTimeout)
case "PROXY_OPTION":
p.opts.proxyOption = parseString(value, defaultProxyOption)
slog.Warn("The PROXY_OPTION environment variable is deprecated, use MEDIA_PROXY_MODE instead")
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
case "MEDIA_PROXY_MODE":
p.opts.mediaProxyMode = parseString(value, defaultMediaProxyMode)
case "PROXY_MEDIA_TYPES":
p.opts.proxyMediaTypes = parseStringList(value, []string{defaultProxyMediaTypes})
// kept for compatibility purpose
slog.Warn("The PROXY_MEDIA_TYPES environment variable is deprecated, use MEDIA_PROXY_RESOURCE_TYPES instead")
p.opts.mediaProxyResourceTypes = parseStringList(value, []string{defaultMediaResourceTypes})
case "MEDIA_PROXY_RESOURCE_TYPES":
p.opts.mediaProxyResourceTypes = parseStringList(value, []string{defaultMediaResourceTypes})
case "PROXY_IMAGE_URL":
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
slog.Warn("The PROXY_IMAGE_URL environment variable is deprecated, use MEDIA_PROXY_CUSTOM_URL instead")
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
case "PROXY_URL":
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
slog.Warn("The PROXY_URL environment variable is deprecated, use MEDIA_PROXY_CUSTOM_URL instead")
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
case "PROXY_PRIVATE_KEY":
slog.Warn("The PROXY_PRIVATE_KEY environment variable is deprecated, use MEDIA_PROXY_PRIVATE_KEY instead")
randomKey := make([]byte, 16)
rand.Read(randomKey)
p.opts.mediaProxyPrivateKey = parseBytes(value, randomKey)
case "MEDIA_PROXY_PRIVATE_KEY":
randomKey := make([]byte, 16)
rand.Read(randomKey)
p.opts.mediaProxyPrivateKey = parseBytes(value, randomKey)
case "MEDIA_PROXY_CUSTOM_URL":
p.opts.mediaProxyCustomURL = parseString(value, defaultMediaProxyURL)
case "CREATE_ADMIN":
p.opts.createAdmin = parseBool(value, defaultCreateAdmin)
case "ADMIN_USERNAME":
@ -230,6 +259,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.metricsPassword = parseString(value, defaultMetricsPassword)
case "METRICS_PASSWORD_FILE":
p.opts.metricsPassword = readSecretFile(value, defaultMetricsPassword)
case "FETCH_NEBULA_WATCH_TIME":
p.opts.fetchNebulaWatchTime = parseBool(value, defaultFetchNebulaWatchTime)
case "FETCH_ODYSEE_WATCH_TIME":
p.opts.fetchOdyseeWatchTime = parseBool(value, defaultFetchOdyseeWatchTime)
case "FETCH_YOUTUBE_WATCH_TIME":
@ -240,10 +271,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.watchdog = parseBool(value, defaultWatchdog)
case "INVIDIOUS_INSTANCE":
p.opts.invidiousInstance = parseString(value, defaultInvidiousInstance)
case "PROXY_PRIVATE_KEY":
randomKey := make([]byte, 16)
rand.Read(randomKey)
p.opts.proxyPrivateKey = parseBytes(value, randomKey)
case "WEBAUTHN":
p.opts.webAuthn = parseBool(value, defaultWebAuthn)
}
}

View File

@ -7,6 +7,7 @@ import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"encoding/hex"
"fmt"
@ -16,8 +17,7 @@ import (
// HashFromBytes returns a SHA-256 checksum of the input.
func HashFromBytes(value []byte) string {
sum := sha256.Sum256(value)
return fmt.Sprintf("%x", sum)
return fmt.Sprintf("%x", sha256.Sum256(value))
}
// Hash returns a SHA-256 checksum of a string.
@ -55,3 +55,12 @@ func GenerateSHA256Hmac(secret string, data []byte) string {
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
func GenerateUUID() string {
b := GenerateRandomBytes(16)
return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
}
func ConstantTimeCmp(a, b string) bool {
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}

View File

@ -783,4 +783,124 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
-- Speed up has_enclosure
CREATE INDEX enclosures_entry_id_idx ON enclosures(entry_id);
-- Speed up unread page
CREATE INDEX entries_user_status_published_idx ON entries(user_id, status, published_at);
CREATE INDEX entries_user_status_created_idx ON entries(user_id, status, created_at);
CREATE INDEX feeds_feed_id_hide_globally_idx ON feeds(id, hide_globally);
-- Speed up history page
CREATE INDEX entries_user_status_changed_published_idx ON entries(user_id, status, changed_at, published_at);
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN rssbridge_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN rssbridge_url text default '';
`
_, err = tx.Exec(sql)
return
},
func(tx *sql.Tx) (err error) {
_, err = tx.Exec(`
CREATE TABLE webauthn_credentials (
handle bytea primary key,
cred_id bytea unique not null,
user_id int references users(id) on delete cascade not null,
public_key bytea not null,
attestation_type varchar(255) not null,
aaguid bytea,
sign_count bigint,
clone_warning bool,
name text,
added_on timestamp with time zone default now(),
last_seen_on timestamp with time zone default now()
);
`)
return
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN omnivore_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN omnivore_api_key text default '';
ALTER TABLE integrations ADD COLUMN omnivore_url text default '';
`
_, err = tx.Exec(sql)
return
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN linkace_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN linkace_url text default '';
ALTER TABLE integrations ADD COLUMN linkace_api_key text default '';
ALTER TABLE integrations ADD COLUMN linkace_tags text default '';
ALTER TABLE integrations ADD COLUMN linkace_is_private bool default 't';
ALTER TABLE integrations ADD COLUMN linkace_check_disabled bool default 't';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN linkwarden_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN linkwarden_url text default '';
ALTER TABLE integrations ADD COLUMN linkwarden_api_key text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN readeck_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN readeck_only_url bool default 'f';
ALTER TABLE integrations ADD COLUMN readeck_url text default '';
ALTER TABLE integrations ADD COLUMN readeck_api_key text default '';
ALTER TABLE integrations ADD COLUMN readeck_labels text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE feeds ADD COLUMN disable_http2 bool default 'f'`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE users ADD COLUMN media_playback_rate numeric default 1;`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
// the WHERE part speed-up the request a lot
sql := `UPDATE entries SET tags = array_remove(tags, '') WHERE '' = ANY(tags);`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
// Entry URLs can exceeds btree maximum size
// Checking entry existence is now using entries_feed_id_status_hash_idx index
_, err = tx.Exec(`DROP INDEX entries_feed_url_idx`)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN raindrop_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN raindrop_token text default '';
ALTER TABLE integrations ADD COLUMN raindrop_collection_id text default '';
ALTER TABLE integrations ADD COLUMN raindrop_tags text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE feeds ADD COLUMN description text default ''`
_, err = tx.Exec(sql)
return err
},
}

View File

@ -1,31 +0,0 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package errors // import "miniflux.app/v2/internal/errors"
import (
"fmt"
"miniflux.app/v2/internal/locale"
)
// LocalizedError represents an error than could be translated to another language.
type LocalizedError struct {
message string
args []interface{}
}
// Error returns untranslated error message.
func (l LocalizedError) Error() string {
return fmt.Sprintf(l.message, l.args...)
}
// Localize returns the translated error message.
func (l LocalizedError) Localize(printer *locale.Printer) string {
return printer.Printf(l.message, l.args...)
}
// NewLocalizedError returns a new LocalizedError.
func NewLocalizedError(message string, args ...interface{}) *LocalizedError {
return &LocalizedError{message: message, args: args}
}

View File

@ -13,8 +13,8 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/integration"
"miniflux.app/v2/internal/mediaproxy"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/proxy"
"miniflux.app/v2/internal/storage"
"github.com/gorilla/mux"
@ -324,7 +324,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
FeedID: entry.FeedID,
Title: entry.Title,
Author: entry.Author,
HTML: proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content),
HTML: mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content),
URL: entry.URL,
IsSaved: isSaved,
IsRead: isRead,

View File

@ -18,8 +18,9 @@ import (
"miniflux.app/v2/internal/http/response/json"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/integration"
"miniflux.app/v2/internal/mediaproxy"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/proxy"
"miniflux.app/v2/internal/reader/fetcher"
mff "miniflux.app/v2/internal/reader/handler"
mfs "miniflux.app/v2/internal/reader/subscription"
"miniflux.app/v2/internal/storage"
@ -122,24 +123,14 @@ const (
LikeStream
)
// Stream defines a stream type and its id
// Stream defines a stream type and its ID.
type Stream struct {
Type StreamType
ID string
}
// RequestModifiers are the parsed request parameters
type RequestModifiers struct {
ExcludeTargets []Stream
FilterTargets []Stream
Streams []Stream
Count int
Offset int
SortDirection string
StartTime int64
StopTime int64
ContinuationToken string
UserID int64
func (s Stream) String() string {
return fmt.Sprintf("%v - '%s'", s.Type, s.ID)
}
func (st StreamType) String() string {
@ -169,34 +160,51 @@ func (st StreamType) String() string {
}
}
func (s Stream) String() string {
return fmt.Sprintf("%v - '%s'", s.Type, s.ID)
// RequestModifiers are the parsed request parameters.
type RequestModifiers struct {
ExcludeTargets []Stream
FilterTargets []Stream
Streams []Stream
Count int
Offset int
SortDirection string
StartTime int64
StopTime int64
ContinuationToken string
UserID int64
}
func (r RequestModifiers) String() string {
result := fmt.Sprintf("UserID: %d\n", r.UserID)
result += fmt.Sprintf("Streams: %d\n", len(r.Streams))
var results []string
results = append(results, fmt.Sprintf("UserID: %d", r.UserID))
var streamStr []string
for _, s := range r.Streams {
result += fmt.Sprintf(" %v\n", s)
streamStr = append(streamStr, s.String())
}
results = append(results, fmt.Sprintf("Streams: [%s]", strings.Join(streamStr, ", ")))
result += fmt.Sprintf("Exclusions: %d\n", len(r.ExcludeTargets))
var exclusions []string
for _, s := range r.ExcludeTargets {
result += fmt.Sprintf(" %v\n", s)
exclusions = append(exclusions, s.String())
}
results = append(results, fmt.Sprintf("Exclusions: [%s]", strings.Join(exclusions, ", ")))
result += fmt.Sprintf("Filter: %d\n", len(r.FilterTargets))
var filters []string
for _, s := range r.FilterTargets {
result += fmt.Sprintf(" %v\n", s)
filters = append(filters, s.String())
}
result += fmt.Sprintf("Count: %d\n", r.Count)
result += fmt.Sprintf("Offset: %d\n", r.Offset)
result += fmt.Sprintf("Sort Direction: %s\n", r.SortDirection)
result += fmt.Sprintf("Continuation Token: %s\n", r.ContinuationToken)
result += fmt.Sprintf("Start Time: %d\n", r.StartTime)
result += fmt.Sprintf("Stop Time: %d\n", r.StopTime)
results = append(results, fmt.Sprintf("Filters: [%s]", strings.Join(filters, ", ")))
return result
results = append(results, fmt.Sprintf("Count: %d", r.Count))
results = append(results, fmt.Sprintf("Offset: %d", r.Offset))
results = append(results, fmt.Sprintf("Sort Direction: %s", r.SortDirection))
results = append(results, fmt.Sprintf("Continuation Token: %s", r.ContinuationToken))
results = append(results, fmt.Sprintf("Start Time: %d", r.StartTime))
results = append(results, fmt.Sprintf("Stop Time: %d", r.StopTime))
return strings.Join(results, "; ")
}
// Serve handles Google Reader API calls.
@ -257,9 +265,10 @@ func getStreamFilterModifiers(r *http.Request) (RequestModifiers, error) {
}
func getStream(streamID string, userID int64) (Stream, error) {
if strings.HasPrefix(streamID, FeedPrefix) {
switch {
case strings.HasPrefix(streamID, FeedPrefix):
return Stream{Type: FeedStream, ID: strings.TrimPrefix(streamID, FeedPrefix)}, nil
} else if strings.HasPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID)) || strings.HasPrefix(streamID, StreamPrefix) {
case strings.HasPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID)) || strings.HasPrefix(streamID, StreamPrefix):
id := strings.TrimPrefix(streamID, fmt.Sprintf(UserStreamPrefix, userID))
id = strings.TrimPrefix(id, StreamPrefix)
switch id {
@ -280,15 +289,15 @@ func getStream(streamID string, userID int64) (Stream, error) {
default:
return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream with id: %s", id)
}
} else if strings.HasPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID)) || strings.HasPrefix(streamID, LabelPrefix) {
case strings.HasPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID)) || strings.HasPrefix(streamID, LabelPrefix):
id := strings.TrimPrefix(streamID, fmt.Sprintf(UserLabelPrefix, userID))
id = strings.TrimPrefix(id, LabelPrefix)
return Stream{LabelStream, id}, nil
} else if streamID == "" {
case streamID == "":
return Stream{NoStream, ""}, nil
default:
return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream type: %s", streamID)
}
return Stream{NoStream, ""}, fmt.Errorf("googlereader: unknown stream type: %s", streamID)
}
func getStreams(streamIDs []string, userID int64) ([]Stream, error) {
@ -374,7 +383,7 @@ func getItemIDs(r *http.Request) ([]int64, error) {
return itemIDs, nil
}
func checkOutputFormat(w http.ResponseWriter, r *http.Request) error {
func checkOutputFormat(r *http.Request) error {
var output string
if r.Method == http.MethodPost {
err := r.ParseForm()
@ -667,15 +676,24 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
return
}
url := r.Form.Get(ParamQuickAdd)
if !validator.IsValidURL(url) {
json.BadRequest(w, r, fmt.Errorf("googlereader: invalid URL: %s", url))
feedURL := r.Form.Get(ParamQuickAdd)
if !validator.IsValidURL(feedURL) {
json.BadRequest(w, r, fmt.Errorf("googlereader: invalid URL: %s", feedURL))
return
}
subscriptions, s_err := mfs.FindSubscriptions(url, "", "", "", "", false, false)
if s_err != nil {
json.ServerError(w, r, s_err)
requestBuilder := fetcher.NewRequestBuilder()
requestBuilder.WithTimeout(config.Opts.HTTPClientTimeout())
requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
var rssBridgeURL string
if intg, err := h.store.Integration(userID); err == nil && intg != nil && intg.RSSBridgeEnabled {
rssBridgeURL = intg.RSSBridgeURL
}
subscriptions, localizedError := mfs.NewSubscriptionFinder(requestBuilder).FindSubscriptions(feedURL, rssBridgeURL)
if localizedError != nil {
json.ServerError(w, r, localizedError.Error())
return
}
@ -719,11 +737,12 @@ func getFeed(stream Stream, store *storage.Storage, userID int64) (*model.Feed,
}
func getOrCreateCategory(category Stream, store *storage.Storage, userID int64) (*model.Category, error) {
if category.ID == "" {
switch {
case category.ID == "":
return store.FirstCategory(userID)
} else if store.CategoryTitleExists(userID, category.ID) {
case store.CategoryTitleExists(userID, category.ID):
return store.CategoryByTitle(userID, category.ID)
} else {
default:
catRequest := model.CategoryRequest{
Title: category.ID,
}
@ -746,9 +765,9 @@ func subscribe(newFeed Stream, category Stream, title string, store *storage.Sto
return nil, verr.Error()
}
created, err := mff.CreateFeed(store, userID, &feedRequest)
if err != nil {
return nil, err
created, localizedError := mff.CreateFeed(store, userID, &feedRequest)
if localizedError != nil {
return nil, localizedError.Error()
}
if title != "" {
@ -855,18 +874,19 @@ func (h *handler) editSubscriptionHandler(w http.ResponseWriter, r *http.Request
}
case "edit":
if title != "" {
err := rename(streamIds[0], title, h.store, userID)
if err != nil {
if err := rename(streamIds[0], title, h.store, userID); err != nil {
json.ServerError(w, r, err)
return
}
} else {
}
if r.Form.Has(ParamTagsAdd) {
if newLabel.Type != LabelStream {
json.BadRequest(w, r, errors.New("destination must be a label"))
return
}
err := move(streamIds[0], newLabel, h.store, userID)
if err != nil {
if err := move(streamIds[0], newLabel, h.store, userID); err != nil {
json.ServerError(w, r, err)
return
}
@ -890,8 +910,8 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
slog.Int64("user_id", userID),
)
if err := checkOutputFormat(w, r); err != nil {
json.ServerError(w, r, err)
if err := checkOutputFormat(r); err != nil {
json.BadRequest(w, r, err)
return
}
@ -942,7 +962,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
}
if len(entries) == 0 {
json.ServerError(w, r, fmt.Errorf("googlereader: no items returned from the database"))
json.BadRequest(w, r, fmt.Errorf("googlereader: no items returned from the database for item IDs: %v", itemIDs))
return
}
@ -966,7 +986,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
}
contentItems := make([]contentItem, len(entries))
for i, entry := range entries {
enclosures := make([]contentItemEnclosure, len(entry.Enclosures))
enclosures := make([]contentItemEnclosure, 0, len(entry.Enclosures))
for _, enclosure := range entry.Enclosures {
enclosures = append(enclosures, contentItemEnclosure{URL: enclosure.URL, Type: enclosure.MimeType})
}
@ -975,7 +995,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
if entry.Feed.Category.Title != "" {
categories = append(categories, fmt.Sprintf(UserLabelPrefix, userID)+entry.Feed.Category.Title)
}
if entry.Starred {
if entry.Status == model.EntryStatusRead {
categories = append(categories, userRead)
}
@ -983,14 +1003,14 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
categories = append(categories, userStarred)
}
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
proxyOption := config.Opts.ProxyOption()
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, r.Host, entry.Content)
proxyOption := config.Opts.MediaProxyMode()
for i := range entry.Enclosures {
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(entry.Enclosures[i].URL) {
for _, mediaType := range config.Opts.ProxyMediaTypes() {
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
entry.Enclosures[i].URL = mediaproxy.ProxifyAbsoluteURL(h.router, r.Host, entry.Enclosures[i].URL)
break
}
}
@ -1001,10 +1021,10 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
ID: fmt.Sprintf(EntryIDLong, entry.ID),
Title: entry.Title,
Author: entry.Author,
TimestampUsec: fmt.Sprintf("%d", entry.Date.UnixNano()/(int64(time.Microsecond)/int64(time.Nanosecond))),
CrawlTimeMsec: fmt.Sprintf("%d", entry.Date.UnixNano()/(int64(time.Microsecond)/int64(time.Nanosecond))),
TimestampUsec: fmt.Sprintf("%d", entry.Date.UnixMicro()),
CrawlTimeMsec: fmt.Sprintf("%d", entry.CreatedAt.UnixMilli()),
Published: entry.Date.Unix(),
Updated: entry.Date.Unix(),
Updated: entry.ChangedAt.Unix(),
Categories: categories,
Canonical: []contentHREF{
{
@ -1152,7 +1172,7 @@ func (h *handler) tagListHandler(w http.ResponseWriter, r *http.Request) {
slog.String("user_agent", r.UserAgent()),
)
if err := checkOutputFormat(w, r); err != nil {
if err := checkOutputFormat(r); err != nil {
json.BadRequest(w, r, err)
return
}
@ -1187,8 +1207,8 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
slog.String("user_agent", r.UserAgent()),
)
if err := checkOutputFormat(w, r); err != nil {
json.ServerError(w, r, err)
if err := checkOutputFormat(r); err != nil {
json.BadRequest(w, r, err)
return
}
@ -1206,7 +1226,7 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
URL: feed.FeedURL,
Categories: []subscriptionCategory{{fmt.Sprintf(UserLabelPrefix, userID) + feed.Category.Title, feed.Category.Title, "folder"}},
HTMLURL: feed.SiteURL,
IconURL: "", //TODO Icons are only base64 encode in DB yet
IconURL: "", // TODO: Icons are base64 encoded in the DB.
})
}
json.OK(w, r, result)
@ -1233,8 +1253,8 @@ func (h *handler) userInfoHandler(w http.ResponseWriter, r *http.Request) {
slog.String("user_agent", r.UserAgent()),
)
if err := checkOutputFormat(w, r); err != nil {
json.ServerError(w, r, err)
if err := checkOutputFormat(r); err != nil {
json.BadRequest(w, r, err)
return
}
@ -1258,8 +1278,8 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
slog.Int64("user_id", userID),
)
if err := checkOutputFormat(w, r); err != nil {
json.ServerError(w, r, fmt.Errorf("googlereader: output only as json supported"))
if err := checkOutputFormat(r); err != nil {
json.BadRequest(w, r, err)
return
}
@ -1269,7 +1289,7 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
return
}
slog.Debug("[GoogleReader] Request modifiers",
slog.Debug("[GoogleReader] Request Modifiers",
slog.String("handler", "streamItemIDsHandler"),
slog.String("client_ip", clientIP),
slog.String("user_agent", r.UserAgent()),
@ -1448,18 +1468,29 @@ func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request
builder.WithLimit(rm.Count)
builder.WithOffset(rm.Offset)
builder.WithSorting(model.DefaultSortingOrder, rm.SortDirection)
if rm.StartTime > 0 {
builder.AfterPublishedDate(time.Unix(rm.StartTime, 0))
}
if rm.StopTime > 0 {
builder.BeforePublishedDate(time.Unix(rm.StopTime, 0))
}
if len(rm.ExcludeTargets) > 0 {
for _, s := range rm.ExcludeTargets {
if s.Type == ReadStream {
builder.WithoutStatus(model.EntryStatusRead)
}
}
}
rawEntryIDs, err := builder.GetEntryIDs()
if err != nil {
json.ServerError(w, r, err)
return
}
var itemRefs = make([]itemRef, 0)
for _, entryID := range rawEntryIDs {
formattedID := strconv.FormatInt(entryID, 10)
@ -1471,6 +1502,7 @@ func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request
json.ServerError(w, r, err)
return
}
continuation := 0
if len(itemRefs)+rm.Offset < totalEntries {
continuation = len(itemRefs) + rm.Offset

View File

@ -1,322 +0,0 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package client // import "miniflux.app/v2/internal/http/client"
import (
"bytes"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"net/url"
"time"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/errors"
)
const (
defaultHTTPClientTimeout = 20
defaultHTTPClientMaxBodySize = 15 * 1024 * 1024
)
var (
errInvalidCertificate = "Invalid SSL certificate (original error: %q)"
errNetworkOperation = "This website is unreachable (original error: %q)"
errRequestTimeout = "Website unreachable, the request timed out after %d seconds"
)
// Client builds and executes HTTP requests.
type Client struct {
inputURL string
requestEtagHeader string
requestLastModifiedHeader string
requestAuthorizationHeader string
requestUsername string
requestPassword string
requestUserAgent string
requestCookie string
customHeaders map[string]string
useProxy bool
doNotFollowRedirects bool
ClientTimeout int
ClientMaxBodySize int64
ClientProxyURL string
AllowSelfSignedCertificates bool
}
// New initializes a new HTTP client.
func New(url string) *Client {
return &Client{
inputURL: url,
ClientTimeout: defaultHTTPClientTimeout,
ClientMaxBodySize: defaultHTTPClientMaxBodySize,
}
}
// NewClientWithConfig initializes a new HTTP client with application config options.
func NewClientWithConfig(url string, opts *config.Options) *Client {
return &Client{
inputURL: url,
requestUserAgent: opts.HTTPClientUserAgent(),
ClientTimeout: opts.HTTPClientTimeout(),
ClientMaxBodySize: opts.HTTPClientMaxBodySize(),
ClientProxyURL: opts.HTTPClientProxy(),
}
}
// WithCredentials defines the username/password for HTTP Basic authentication.
func (c *Client) WithCredentials(username, password string) *Client {
if username != "" && password != "" {
c.requestUsername = username
c.requestPassword = password
}
return c
}
// WithCustomHeaders defines custom HTTP headers.
func (c *Client) WithCustomHeaders(customHeaders map[string]string) *Client {
c.customHeaders = customHeaders
return c
}
// WithCacheHeaders defines caching headers.
func (c *Client) WithCacheHeaders(etagHeader, lastModifiedHeader string) *Client {
c.requestEtagHeader = etagHeader
c.requestLastModifiedHeader = lastModifiedHeader
return c
}
// WithProxy enables proxy for the current HTTP request.
func (c *Client) WithProxy() *Client {
c.useProxy = true
return c
}
// WithoutRedirects disables HTTP redirects.
func (c *Client) WithoutRedirects() *Client {
c.doNotFollowRedirects = true
return c
}
// WithUserAgent defines the User-Agent header to use for HTTP requests.
func (c *Client) WithUserAgent(userAgent string) *Client {
if userAgent != "" {
c.requestUserAgent = userAgent
}
return c
}
// WithCookie defines the Cookies to use for HTTP requests.
func (c *Client) WithCookie(cookie string) *Client {
if cookie != "" {
c.requestCookie = cookie
}
return c
}
// Get performs a GET HTTP request.
func (c *Client) Get() (*Response, error) {
request, err := c.buildRequest(http.MethodGet, nil)
if err != nil {
return nil, err
}
return c.executeRequest(request)
}
func (c *Client) executeRequest(request *http.Request) (*Response, error) {
startTime := time.Now()
slog.Debug("Executing outgoing HTTP request",
slog.Group("request",
slog.String("method", request.Method),
slog.String("url", request.URL.String()),
slog.String("user_agent", request.UserAgent()),
slog.Bool("is_authenticated", c.requestAuthorizationHeader != "" || (c.requestUsername != "" && c.requestPassword != "")),
slog.Bool("has_cookie", c.requestCookie != ""),
slog.Bool("with_redirects", !c.doNotFollowRedirects),
slog.Bool("with_proxy", c.useProxy),
slog.String("proxy_url", c.ClientProxyURL),
slog.Bool("with_caching_headers", c.requestEtagHeader != "" || c.requestLastModifiedHeader != ""),
),
)
client := c.buildClient()
resp, err := client.Do(request)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
if uerr, ok := err.(*url.Error); ok {
switch uerr.Err.(type) {
case x509.CertificateInvalidError, x509.HostnameError:
err = errors.NewLocalizedError(errInvalidCertificate, uerr.Err)
case *net.OpError:
err = errors.NewLocalizedError(errNetworkOperation, uerr.Err)
case net.Error:
nerr := uerr.Err.(net.Error)
if nerr.Timeout() {
err = errors.NewLocalizedError(errRequestTimeout, c.ClientTimeout)
}
}
}
return nil, err
}
if resp.ContentLength > c.ClientMaxBodySize {
return nil, fmt.Errorf("client: response too large (%d bytes)", resp.ContentLength)
}
buf, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("client: error while reading body %v", err)
}
response := &Response{
Body: bytes.NewReader(buf),
StatusCode: resp.StatusCode,
EffectiveURL: resp.Request.URL.String(),
LastModified: resp.Header.Get("Last-Modified"),
ETag: resp.Header.Get("ETag"),
Expires: resp.Header.Get("Expires"),
ContentType: resp.Header.Get("Content-Type"),
ContentLength: resp.ContentLength,
}
slog.Debug("Completed outgoing HTTP request",
slog.Duration("duration", time.Since(startTime)),
slog.Group("request",
slog.String("method", request.Method),
slog.String("url", request.URL.String()),
slog.String("user_agent", request.UserAgent()),
slog.Bool("is_authenticated", c.requestAuthorizationHeader != "" || (c.requestUsername != "" && c.requestPassword != "")),
slog.Bool("has_cookie", c.requestCookie != ""),
slog.Bool("with_redirects", !c.doNotFollowRedirects),
slog.Bool("with_proxy", c.useProxy),
slog.String("proxy_url", c.ClientProxyURL),
slog.Bool("with_caching_headers", c.requestEtagHeader != "" || c.requestLastModifiedHeader != ""),
),
slog.Group("response",
slog.Int("status_code", response.StatusCode),
slog.String("effective_url", response.EffectiveURL),
slog.String("content_type", response.ContentType),
slog.Int64("content_length", response.ContentLength),
slog.String("last_modified", response.LastModified),
slog.String("etag", response.ETag),
slog.String("expires", response.Expires),
),
)
// Ignore caching headers for feeds that do not want any cache.
if resp.Header.Get("Expires") == "0" {
response.ETag = ""
response.LastModified = ""
}
return response, err
}
func (c *Client) buildRequest(method string, body io.Reader) (*http.Request, error) {
request, err := http.NewRequest(method, c.inputURL, body)
if err != nil {
return nil, err
}
request.Header = c.buildHeaders()
if c.requestUsername != "" && c.requestPassword != "" {
request.SetBasicAuth(c.requestUsername, c.requestPassword)
}
return request, nil
}
func (c *Client) buildClient() http.Client {
client := http.Client{
Timeout: time.Duration(c.ClientTimeout) * time.Second,
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
// Default is 30s.
Timeout: 10 * time.Second,
// Default is 30s.
KeepAlive: 15 * time.Second,
}).DialContext,
// Default is 100.
MaxIdleConns: 50,
// Default is 90s.
IdleConnTimeout: 10 * time.Second,
}
if c.AllowSelfSignedCertificates {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
if c.doNotFollowRedirects {
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
}
if c.useProxy && c.ClientProxyURL != "" {
proxyURL, err := url.Parse(c.ClientProxyURL)
if err != nil {
slog.Error("Unable to parse proxy URL",
slog.String("proxy_url", c.ClientProxyURL),
slog.Any("error", err),
)
} else {
transport.Proxy = http.ProxyURL(proxyURL)
}
}
client.Transport = transport
return client
}
func (c *Client) buildHeaders() http.Header {
headers := make(http.Header)
headers.Add("Accept", "*/*")
if c.requestUserAgent != "" {
headers.Add("User-Agent", c.requestUserAgent)
}
if c.requestEtagHeader != "" {
headers.Add("If-None-Match", c.requestEtagHeader)
}
if c.requestLastModifiedHeader != "" {
headers.Add("If-Modified-Since", c.requestLastModifiedHeader)
}
if c.requestAuthorizationHeader != "" {
headers.Add("Authorization", c.requestAuthorizationHeader)
}
if c.requestCookie != "" {
headers.Add("Cookie", c.requestCookie)
}
for key, value := range c.customHeaders {
headers.Add(key, value)
}
headers.Add("Connection", "close")
return headers
}

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package client // import "miniflux.app/v2/internal/http/client"
import (
"fmt"
"net/http/httptest"
"os"
"testing"
"github.com/mccutchen/go-httpbin/v2/httpbin"
)
var srv *httptest.Server
func TestMain(m *testing.M) {
srv = httptest.NewServer(httpbin.New())
exitCode := m.Run()
srv.Close()
os.Exit(exitCode)
}
func MakeClient(path string) *Client {
return New(fmt.Sprintf("%s%s", srv.URL, path))
}
func TestClientWithDelay(t *testing.T) {
clt := MakeClient("/delay/5")
clt.ClientTimeout = 1
_, err := clt.Get()
if err == nil {
t.Fatal(`The client should stops after 1 second`)
}
}
func TestClientWithError(t *testing.T) {
clt := MakeClient("/status/502")
clt.ClientTimeout = 5
response, err := clt.Get()
if err != nil {
t.Fatal(err)
}
if response.StatusCode != 502 {
t.Fatalf(`Unexpected response status code: %d`, response.StatusCode)
}
if !response.HasServerFailure() {
t.Fatal(`A 502 error is considered as server failure`)
}
}
func TestClientWithResponseTooLarge(t *testing.T) {
clt := MakeClient("/bytes/100")
clt.ClientMaxBodySize = 10
_, err := clt.Get()
if err == nil {
t.Fatal(`The client should fails when reading a response too large`)
}
}
func TestClientWithBasicAuth(t *testing.T) {
clt := MakeClient("/basic-auth/testuser/testpassword")
clt.WithCredentials("testuser", "testpassword")
_, err := clt.Get()
if err != nil {
t.Fatalf(`The client should be authenticated successfully: %v`, err)
}
}

View File

@ -1,119 +0,0 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package client // import "miniflux.app/v2/internal/http/client"
import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
"unicode/utf8"
"golang.org/x/net/html/charset"
)
var xmlEncodingRegex = regexp.MustCompile(`<\?xml(.*)encoding=["'](.+)["'](.*)\?>`)
// Response wraps a server response.
type Response struct {
Body io.Reader
StatusCode int
EffectiveURL string
LastModified string
ETag string
Expires string
ContentType string
ContentLength int64
}
func (r *Response) String() string {
return fmt.Sprintf(
`StatusCode=%d EffectiveURL=%q LastModified=%q ETag=%s Expires=%s ContentType=%q ContentLength=%d`,
r.StatusCode,
r.EffectiveURL,
r.LastModified,
r.ETag,
r.Expires,
r.ContentType,
r.ContentLength,
)
}
// IsNotFound returns true if the resource doesn't exist anymore.
func (r *Response) IsNotFound() bool {
return r.StatusCode == 404 || r.StatusCode == 410
}
// IsNotAuthorized returns true if the resource require authentication.
func (r *Response) IsNotAuthorized() bool {
return r.StatusCode == 401
}
// HasServerFailure returns true if the status code represents a failure.
func (r *Response) HasServerFailure() bool {
return r.StatusCode >= 400
}
// IsModified returns true if the resource has been modified.
func (r *Response) IsModified(etag, lastModified string) bool {
if r.StatusCode == 304 {
return false
}
if r.ETag != "" && r.ETag == etag {
return false
}
if r.LastModified != "" && r.LastModified == lastModified {
return false
}
return true
}
// EnsureUnicodeBody makes sure the body is encoded in UTF-8.
//
// If a charset other than UTF-8 is detected, we convert the document to UTF-8.
// This is used by the scraper and feed readers.
//
// Do not forget edge cases:
//
// - Feeds with encoding specified only in Content-Type header and not in XML document
// - Feeds with encoding specified in both places
// - Feeds with encoding specified only in XML document and not in HTTP header
// - Feeds with wrong encoding defined and already in UTF-8
func (r *Response) EnsureUnicodeBody() (err error) {
buffer, err := io.ReadAll(r.Body)
if err != nil {
return err
}
r.Body = bytes.NewReader(buffer)
if utf8.Valid(buffer) {
return nil
}
if strings.Contains(r.ContentType, "xml") {
// We ignore documents with encoding specified in XML prolog.
// This is going to be handled by the XML parser.
length := 1024
if len(buffer) < 1024 {
length = len(buffer)
}
if xmlEncodingRegex.Match(buffer[0:length]) {
return nil
}
}
r.Body, err = charset.NewReader(r.Body, r.ContentType)
return err
}
// BodyAsString returns the response body as string.
func (r *Response) BodyAsString() string {
bytes, _ := io.ReadAll(r.Body)
return string(bytes)
}

View File

@ -1,148 +0,0 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package client // import "miniflux.app/v2/internal/http/client"
import (
"bytes"
"os"
"strings"
"testing"
"unicode/utf8"
)
func TestIsNotFound(t *testing.T) {
scenarios := map[int]bool{
200: false,
404: true,
410: true,
}
for input, expected := range scenarios {
r := &Response{StatusCode: input}
actual := r.IsNotFound()
if actual != expected {
t.Errorf(`Unexpected result, got %v instead of %v for status code %d`, actual, expected, input)
}
}
}
func TestIsNotAuthorized(t *testing.T) {
scenarios := map[int]bool{
200: false,
401: true,
403: false,
}
for input, expected := range scenarios {
r := &Response{StatusCode: input}
actual := r.IsNotAuthorized()
if actual != expected {
t.Errorf(`Unexpected result, got %v instead of %v for status code %d`, actual, expected, input)
}
}
}
func TestHasServerFailure(t *testing.T) {
scenarios := map[int]bool{
200: false,
404: true,
500: true,
}
for input, expected := range scenarios {
r := &Response{StatusCode: input}
actual := r.HasServerFailure()
if actual != expected {
t.Errorf(`Unexpected result, got %v instead of %v for status code %d`, actual, expected, input)
}
}
}
func TestIsModifiedWith304Status(t *testing.T) {
r := &Response{StatusCode: 304}
if r.IsModified("etag", "lastModified") {
t.Error("The resource should not be considered modified")
}
}
func TestIsModifiedWithIdenticalEtag(t *testing.T) {
r := &Response{StatusCode: 200, ETag: "etag"}
if r.IsModified("etag", "lastModified") {
t.Error("The resource should not be considered modified")
}
}
func TestIsModifiedWithIdenticalLastModified(t *testing.T) {
r := &Response{StatusCode: 200, LastModified: "lastModified"}
if r.IsModified("etag", "lastModified") {
t.Error("The resource should not be considered modified")
}
}
func TestIsModifiedWithDifferentHeaders(t *testing.T) {
r := &Response{StatusCode: 200, ETag: "some etag", LastModified: "some date"}
if !r.IsModified("etag", "lastModified") {
t.Error("The resource should be considered modified")
}
}
func TestToString(t *testing.T) {
input := `test`
r := &Response{Body: strings.NewReader(input)}
if r.BodyAsString() != input {
t.Error(`Unexpected output`)
}
}
func TestEnsureUnicodeWithHTMLDocuments(t *testing.T) {
var unicodeTestCases = []struct {
filename, contentType string
convertedToUnicode bool
}{
{"HTTP-charset.html", "text/html; charset=iso-8859-15", true},
{"UTF-16LE-BOM.html", "", true},
{"UTF-16BE-BOM.html", "", true},
{"meta-content-attribute.html", "text/html", true},
{"meta-charset-attribute.html", "text/html", true},
{"No-encoding-declaration.html", "text/html", true},
{"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", true},
{"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", true},
{"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", true},
{"UTF-8-BOM-vs-meta-content.html", "text/html", true},
{"UTF-8-BOM-vs-meta-charset.html", "text/html", true},
{"windows_1251.html", "text/html; charset=windows-1251", true},
{"gb2312.html", "text/html", true},
{"urdu.xml", "text/xml; charset=utf-8", true},
{"content-type-only-win-8859-1.xml", "application/xml; charset=ISO-8859-1", true},
{"rdf_utf8.xml", "application/rss+xml; charset=utf-8", true},
{"rdf_utf8.xml", "application/rss+xml; charset: utf-8", true}, // Invalid Content-Type
{"charset-content-type-xml-iso88591.xml", "application/rss+xml; charset=ISO-8859-1", false},
{"windows_1251.xml", "text/xml", false},
{"smallfile.xml", "text/xml; charset=utf-8", true},
{"single_quote_xml_encoding.xml", "text/xml; charset=utf-8", true},
}
for _, tc := range unicodeTestCases {
content, err := os.ReadFile("testdata/" + tc.filename)
if err != nil {
t.Fatalf(`Unable to read file %q: %v`, tc.filename, err)
}
r := &Response{Body: bytes.NewReader(content), ContentType: tc.contentType}
parseErr := r.EnsureUnicodeBody()
if parseErr != nil {
t.Fatalf(`Unicode conversion error for %q - %q: %v`, tc.filename, tc.contentType, parseErr)
}
isUnicode := utf8.ValidString(r.BodyAsString())
if isUnicode != tc.convertedToUnicode {
t.Errorf(`Unicode conversion %q - %q, got: %v, expected: %v`,
tc.filename, tc.contentType, isUnicode, tc.convertedToUnicode)
}
}
}

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<title>HTTP charset</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="The character encoding of a page can be set using the HTTP header charset declaration.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
</head>
<body>
<p class='title'>HTTP charset</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">The character encoding of a page can be set using the HTTP header charset declaration.</p>
<div class="notes"><p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p><p>The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-003">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-001<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-001" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<title>HTTP vs UTF-8 BOM</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
</head>
<body>
<p class='title'>HTTP vs UTF-8 BOM</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.</p>
<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p><p>If the test is unsuccessful, the characters &#x00EF;&#x00BB;&#x00BF; should appear at the top of the page. These represent the bytes that make up the UTF-8 signature when encountered in the ISO 8859-15 encoding.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-022">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-034<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-034" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="iso-8859-1" > <title>HTTP vs meta charset</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.">
<style type='text/css'>
.test div { width: 50px; }.test div { width: 90px; }
</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
</head>
<body>
<p class='title'>HTTP vs meta charset</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.</p>
<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-1.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-037">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-018<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-018" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1" > <title>HTTP vs meta content</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.">
<style type='text/css'>
.test div { width: 50px; }.test div { width: 90px; }
</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
</head>
<body>
<p class='title'>HTTP vs meta content</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.</p>
<div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-1.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-018">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-016<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-016" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<title>No encoding declaration</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
</head>
<body>
<p class='title'>No encoding declaration</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.</p>
<div class="notes"><p><p>The test on this page contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-034">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-015<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-015" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,9 +0,0 @@
These test cases come from
http://www.w3.org/International/tests/repository/html5/the-input-byte-stream/results-basics
Distributed under both the W3C Test Suite License
(http://www.w3.org/Consortium/Legal/2008/04-testsuite-license)
and the W3C 3-clause BSD License
(http://www.w3.org/Consortium/Legal/2008/03-bsd-license).
To contribute to a W3C Test Suite, see the policies and contribution
forms (http://www.w3.org/2004/10/27-testcases).

Binary file not shown.

Binary file not shown.

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="iso-8859-15"> <title>UTF-8 BOM vs meta charset</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.">
<style type='text/css'>
.test div { width: 50px; }.test div { width: 90px; }
</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
</head>
<body>
<p class='title'>UTF-8 BOM vs meta charset</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.</p>
<div class="notes"><p><p>The page contains an encoding declaration in a meta charset attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-024">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-038<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-038" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>UTF-8 BOM vs meta content</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css">
</head>
<body>
<p class='title'>UTF-8 BOM vs meta content</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.</p>
<div class="notes"><p><p>The page contains an encoding declaration in a meta content attribute that attempts to set the character encoding to ISO 8859-15, but the file starts with a UTF-8 signature.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. This matches the sequence of bytes above when they are interpreted as UTF-8. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-038">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-037<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-037" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,422 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generator="FeedCreator 1.6" -->
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:atom="http://www.w3.org/2005/Atom"
version="2.0">
<channel>
<title>Golem.de</title>
<description>IT-News fuer Profis</description>
<link>https://www.golem.de/</link>
<atom:link rel="self" href="https://rss.golem.de/rss.php?feed=RSS2.0" />
<lastBuildDate>Sun, 28 Oct 2018 13:49:01 +0100</lastBuildDate>
<generator>FeedCreator 1.6</generator>
<image>
<url>https://www.golem.de/staticrl/images/golem-rss.png</url>
<title>Golem.de</title>
<link>https://www.golem.de/</link>
<description>Golem.de News Feed</description>
</image>
<language>de</language>
<atom:link rel="hub" href="http://golem.superfeedr.com/" />
<item>
<title>Red Dead Redemption 2: Hinweise auf PC-Umsetzung in App von Rockstar Games</title>
<link>https://www.golem.de/news/red-dead-redemption-2-hinweise-auf-pc-umsetzung-in-app-von-rockstar-games-1810-137358-rss.html</link>
<description>Viele Spieler wünschen sich eine PC-Version von Red Dead Redemption 2, aber Entwickler Rockstar Games schweigt zu dem Thema. Anders die offizielle Companion App: In einigen ihrer Daten gibt es Hinweise auf die Umsetzung. (&lt;a href=&quot;https://www.golem.de/specials/red-dead-redemption-2/&quot;&gt;Red Dead Redemption 2&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/red-dead-redemption/&quot;&gt;Red Dead Redemption&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137358&amp;amp;page=1&amp;amp;ts=1540730880&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<pubDate>Sun, 28 Oct 2018 13:48:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137358-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137358-177541-177538_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Viele Spieler wünschen sich eine PC-Version von Red Dead Redemption 2, aber Entwickler Rockstar Games schweigt zu dem Thema. Anders die offizielle Companion App: In einigen ihrer Daten gibt es Hinweise auf die Umsetzung. (<a href="https://www.golem.de/specials/red-dead-redemption-2/">Red Dead Redemption 2</a>, <a href="https://www.golem.de/specials/red-dead-redemption/">Red Dead Redemption</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137358&amp;page=1&amp;ts=1540730880" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments />
</item>
<item>
<title>Let's Play: Twitch will Streamer zusammen spielen und singen lassen</title>
<link>https://www.golem.de/news/let-s-play-twitch-will-streamer-zusammen-spielen-und-singen-lassen-1810-137357-rss.html</link>
<description>Der Streamingdienst Twitch hat auf seiner Hausmesse neue Funktionen für Kanalbetreiber und Zuschauer vorgestellt. Unter anderem soll es künftig Übertragungen mit bis zu vier Spielern geben - und Singwettbewerbe. (&lt;a href=&quot;https://www.golem.de/specials/twitch/&quot;&gt;Twitch&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/amazon/&quot;&gt;Amazon&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137357&amp;amp;page=1&amp;amp;ts=1540728000&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/games/let-s-play-twitch-will-streamer-zusammen-spielen-und-singen-lassen/121579,list.html</comments>
<pubDate>Sun, 28 Oct 2018 13:00:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137357-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137357-177536-177533_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Der Streamingdienst Twitch hat auf seiner Hausmesse neue Funktionen für Kanalbetreiber und Zuschauer vorgestellt. Unter anderem soll es künftig Übertragungen mit bis zu vier Spielern geben - und Singwettbewerbe. (<a href="https://www.golem.de/specials/twitch/">Twitch</a>, <a href="https://www.golem.de/specials/amazon/">Amazon</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137357&amp;page=1&amp;ts=1540728000" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments />
</item>
<item>
<title>Zhuque-1: Erste private chinesische Satellitenmission fehlgeschlagen</title>
<link>https://www.golem.de/news/zhuque-1-erste-private-chinesische-satellitenmission-fehlgeschlagen-1810-137356-rss.html</link>
<description>Die Zhuque-1 hat es nicht in den Orbit geschafft: Beim Zünden der dritten Raketenstufe kam es zu Problemen. Bei einem Erfolg wäre der Hersteller Landspace das erste von rund 60 kommerziellen chinesischen Unternehmen gewesen, das einen Satelliten ins All gebracht hätte. (&lt;a href=&quot;https://www.golem.de/specials/raumfahrt/&quot;&gt;Raumfahrt&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/internet/&quot;&gt;Internet&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137356&amp;amp;page=1&amp;amp;ts=1540722420&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/internet/zhuque-1-erste-private-chinesische-satellitenmission-fehlgeschlagen/121578,list.html</comments>
<pubDate>Sun, 28 Oct 2018 11:27:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137356-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137356-177532-177529_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die Zhuque-1 hat es nicht in den Orbit geschafft: Beim Zünden der dritten Raketenstufe kam es zu Problemen. Bei einem Erfolg wäre der Hersteller Landspace das erste von rund 60 kommerziellen chinesischen Unternehmen gewesen, das einen Satelliten ins All gebracht hätte. (<a href="https://www.golem.de/specials/raumfahrt/">Raumfahrt</a>, <a href="https://www.golem.de/specials/internet/">Internet</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137356&amp;page=1&amp;ts=1540722420" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>1</slash:comments>
</item>
<item>
<title>City Transformer: Startup entwickelt faltbares Elektroauto gegen Parkplatznot</title>
<link>https://www.golem.de/news/city-transformer-startup-entwickelt-faltbares-elektroauto-gegen-parkplatznot-1810-137355-rss.html</link>
<description>Es passt fast in jede Parklücke: Ein Faltauto des Startups City Transformer soll Städtern künftig das Leben erleichtern. Das innovative Fahrzeug wird zusammen mit Yamaha entwickelt. Vorbestellungen sollen voraussichtlich ab 2020 möglich sein, mehrere Versionen sind geplant. (&lt;a href=&quot;https://www.golem.de/specials/elektromobilitaet/&quot;&gt;Elektromobilität&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/elektroauto/&quot;&gt;Elektroauto&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137355&amp;amp;page=1&amp;amp;ts=1540721400&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/automobil/city-transformer-startup-entwickelt-faltbares-elektroauto-gegen-parkplatznot/121577,list.html</comments>
<pubDate>Sun, 28 Oct 2018 11:10:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137355-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137355-177527-177524_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Es passt fast in jede Parklücke: Ein Faltauto des Startups City Transformer soll Städtern künftig das Leben erleichtern. Das innovative Fahrzeug wird zusammen mit Yamaha entwickelt. Vorbestellungen sollen voraussichtlich ab 2020 möglich sein, mehrere Versionen sind geplant. (<a href="https://www.golem.de/specials/elektromobilitaet/">Elektromobilität</a>, <a href="https://www.golem.de/specials/elektroauto/">Elektroauto</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137355&amp;page=1&amp;ts=1540721400" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>37</slash:comments>
</item>
<item>
<title>Machine Learning: Von KI erstelltes Porträt für 432.500 US.Dollar versteigert</title>
<link>https://www.golem.de/news/machine-learning-von-ki-erstelltes-portraet-fuer-432-500-us-dollar-versteigert-1810-137353-rss.html</link>
<description>Kann Software Kunst erstellen? Eine erfolgreiche Auktion beweist, dass es zumindest Abnehmer dafür gibt. Allerdings hat sich das Entwicklerteam Obvious wohl stark bei anderen KI-Systemen bedient. (&lt;a href=&quot;https://www.golem.de/specials/neuronalesnetzwerk/&quot;&gt;Neuronales Netzwerk&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/ki/&quot;&gt;KI&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137353&amp;amp;page=1&amp;amp;ts=1540643580&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/applikationen/machine-learning-von-ki-erstelltes-portraet-fuer-432.500-us.dollar-versteigert/121575,list.html</comments>
<pubDate>Sat, 27 Oct 2018 13:33:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137353-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137353-177523-177520_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Kann Software Kunst erstellen? Eine erfolgreiche Auktion beweist, dass es zumindest Abnehmer dafür gibt. Allerdings hat sich das Entwicklerteam Obvious wohl stark bei anderen KI-Systemen bedient. (<a href="https://www.golem.de/specials/neuronalesnetzwerk/">Neuronales Netzwerk</a>, <a href="https://www.golem.de/specials/ki/">KI</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137353&amp;page=1&amp;ts=1540643580" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>31</slash:comments>
</item>
<item>
<title>Projekt Jedi: Microsoft will weiter mit US-Militär zusammenarbeiten</title>
<link>https://www.golem.de/news/project-jedi-microsoft-will-weiter-mit-us-militaer-zusammenarbeiten-1810-137352-rss.html</link>
<description>In einem Blogbeitrag hat sich Microsoft-Präsident Brad Smith zur Zusammenarbeit mit dem US-Verteidigungsministerium bekannt. Mitarbeiter, die nicht an derartigen Projekten arbeiten wollen, sollen in andere Bereiche des Unternehmens wechseln können. (&lt;a href=&quot;https://www.golem.de/specials/microsoft/&quot;&gt;Microsoft&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/google/&quot;&gt;Google&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137352&amp;amp;page=1&amp;amp;ts=1540641780&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/internet/projekt-jedi-microsoft-will-weiter-mit-us-militaer-zusammenarbeiten/121574,list.html</comments>
<pubDate>Sat, 27 Oct 2018 13:03:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137352-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137055-176130-176127_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">In einem Blogbeitrag hat sich Microsoft-Präsident Brad Smith zur Zusammenarbeit mit dem US-Verteidigungsministerium bekannt. Mitarbeiter, die nicht an derartigen Projekten arbeiten wollen, sollen in andere Bereiche des Unternehmens wechseln können. (<a href="https://www.golem.de/specials/microsoft/">Microsoft</a>, <a href="https://www.golem.de/specials/google/">Google</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137352&amp;page=1&amp;ts=1540641780" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>21</slash:comments>
</item>
<item>
<title>Star Wars: Boba-Fett-Film ist &quot;zu 100 Prozent tot&quot;</title>
<link>https://www.golem.de/news/star-wars-boba-fett-film-ist-zu-100-prozent-tot-1810-137351-rss.html</link>
<description>Es wird wohl doch keinen dritten Star-Wars-Ableger geben, der sich um den kultigen Kopfgeldjäger Boba Fett dreht. Das wird laut einem Medienbericht teils auf den geringen Erfolg des Han-Solo-Films zurückgeführt. Stattdessen soll ein bisher unbekannter Charakter in einer Serie die mandalorianische Rüstung anziehen. (&lt;a href=&quot;https://www.golem.de/specials/star-wars/&quot;&gt;Star Wars&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/film/&quot;&gt;Film&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137351&amp;amp;page=1&amp;amp;ts=1540639620&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/audio-video/star-wars-boba-fett-film-ist-zu-100-prozent-tot/121573,list.html</comments>
<pubDate>Sat, 27 Oct 2018 12:27:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137351-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137351-177519-177514_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Es wird wohl doch keinen dritten Star-Wars-Ableger geben, der sich um den kultigen Kopfgeldjäger Boba Fett dreht. Das wird laut einem Medienbericht teils auf den geringen Erfolg des Han-Solo-Films zurückgeführt. Stattdessen soll ein bisher unbekannter Charakter in einer Serie die mandalorianische Rüstung anziehen. (<a href="https://www.golem.de/specials/star-wars/">Star Wars</a>, <a href="https://www.golem.de/specials/film/">Film</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137351&amp;page=1&amp;ts=1540639620" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>148</slash:comments>
</item>
<item>
<title>Lenovo: Fehlerhafte Bios-Einstellung macht Thinkpads unbrauchbar</title>
<link>https://www.golem.de/news/lenovo-fehlerhafte-bios-einstellung-macht-thinkpads-unbrauchbar-1810-137350-rss.html</link>
<description>Die Bios-Unterstützung für Thunderbolt bei Thinkpads zu aktivieren, ist derzeit keine gute Idee: Mehrere Nutzer berichten von nicht mehr startenden Notebooks, nachdem sie diese Funktion aktiviert haben. Das konnte auf diversen Linux-Distributionen, aber auch mit Windows 10 repliziert werden. (&lt;a href=&quot;https://www.golem.de/specials/lenovo/&quot;&gt;Lenovo&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/business-notebooks/&quot;&gt;Business-Notebooks&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137350&amp;amp;page=1&amp;amp;ts=1540633200&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/applikationen/lenovo-fehlerhafte-bios-einstellung-macht-thinkpads-unbrauchbar/121572,list.html</comments>
<pubDate>Sat, 27 Oct 2018 10:40:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137350-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137350-177513-177510_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die Bios-Unterstützung für Thunderbolt bei Thinkpads zu aktivieren, ist derzeit keine gute Idee: Mehrere Nutzer berichten von nicht mehr startenden Notebooks, nachdem sie diese Funktion aktiviert haben. Das konnte auf diversen Linux-Distributionen, aber auch mit Windows 10 repliziert werden. (<a href="https://www.golem.de/specials/lenovo/">Lenovo</a>, <a href="https://www.golem.de/specials/business-notebooks/">Business-Notebooks</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137350&amp;page=1&amp;ts=1540633200" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>16</slash:comments>
</item>
<item>
<title>Wochenrückblick: Wilder Westen, buntes Handy, nutzloses Siegel</title>
<link>https://www.golem.de/news/wochenrueckblick-wilder-westen-buntes-handy-nutzloses-siegel-1810-137318-rss.html</link>
<description> Wir testen das iPhone Xr, sind ein Revolverheld und entdecken wieder Sicherheitslücken. Sieben Tage und viele Meldungen im Überblick. (&lt;a href=&quot;https://www.golem.de/specials/golemwochenrueckblick/&quot;&gt;Golem-Wochenrückblick&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/business-notebooks/&quot;&gt;Business-Notebooks&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137318&amp;amp;page=1&amp;amp;ts=1540623720&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/politik-recht/wochenrueckblick-wilder-westen-buntes-handy-nutzloses-siegel/121571,list.html</comments>
<pubDate>Sat, 27 Oct 2018 08:02:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137318-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137318-177504-177501_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left"> Wir testen das iPhone Xr, sind ein Revolverheld und entdecken wieder Sicherheitslücken. Sieben Tage und viele Meldungen im Überblick. (<a href="https://www.golem.de/specials/golemwochenrueckblick/">Golem-Wochenrückblick</a>, <a href="https://www.golem.de/specials/business-notebooks/">Business-Notebooks</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137318&amp;page=1&amp;ts=1540623720" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments />
</item>
<item>
<title>Fernsehen: 5G-Netz wird so wichtig wie Strom und Wasser</title>
<link>https://www.golem.de/news/fernsehen-5g-netz-wird-so-wichtig-wie-strom-und-wasser-1810-137349-rss.html</link>
<description>Ein 5G-FeMBMS-Sendernetz für die Fernsehverbreitung sorgt für Aufsehen, noch bevor man weiß, ob es funktioniert. Wie Rundfunkübertragung und Mobilfunk zusammenkommen können, wurde auf den Medientagen München besprochen. (&lt;a href=&quot;https://www.golem.de/specials/fernsehen/&quot;&gt;Fernsehen&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/technologie/&quot;&gt;Technologie&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137349&amp;amp;page=1&amp;amp;ts=1540572960&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/handy/fernsehen-5g-netz-wird-so-wichtig-wie-strom-und-wasser/121570,list.html</comments>
<pubDate>Fri, 26 Oct 2018 17:56:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137349-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137349-177509-177506_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Ein 5G-FeMBMS-Sendernetz für die Fernsehverbreitung sorgt für Aufsehen, noch bevor man weiß, ob es funktioniert. Wie Rundfunkübertragung und Mobilfunk zusammenkommen können, wurde auf den Medientagen München besprochen. (<a href="https://www.golem.de/specials/fernsehen/">Fernsehen</a>, <a href="https://www.golem.de/specials/technologie/">Technologie</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137349&amp;page=1&amp;ts=1540572960" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>25</slash:comments>
</item>
<item>
<title>Linux und BSD: Sicherheitslücke in X.org ermöglicht Root-Rechte</title>
<link>https://www.golem.de/news/linux-und-bsd-sicherheitsluecke-in-x-org-ermoeglicht-root-rechte-1810-137347-rss.html</link>
<description>Eine Sicherheitslücke im Displayserver X.org erlaubt unter bestimmten Umständen das Überschreiben von Dateien und das Ausweiten der Benutzerrechte. Der passende Exploit passt in einen Tweet. (&lt;a href=&quot;https://www.golem.de/specials/sicherheitsluecke/&quot;&gt;Sicherheitslücke&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/openbsd/&quot;&gt;OpenBSD&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137347&amp;amp;page=1&amp;amp;ts=1540564620&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/linux-und-bsd-sicherheitsluecke-in-x.org-ermoeglicht-root-rechte/121569,list.html</comments>
<pubDate>Fri, 26 Oct 2018 15:37:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137347-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137347-177500-177497_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Eine Sicherheitslücke im Displayserver X.org erlaubt unter bestimmten Umständen das Überschreiben von Dateien und das Ausweiten der Benutzerrechte. Der passende Exploit passt in einen Tweet. (<a href="https://www.golem.de/specials/sicherheitsluecke/">Sicherheitslücke</a>, <a href="https://www.golem.de/specials/openbsd/">OpenBSD</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137347&amp;page=1&amp;ts=1540564620" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>47</slash:comments>
</item>
<item>
<title>Augsburg: Fujitsu Deutschland macht alles dicht</title>
<link>https://www.golem.de/news/augsburg-fujitsu-deutschland-macht-alles-dicht-1810-137348-rss.html</link>
<description>Fujitsu will seine gesamte Fertigung außerhalb Japans schließen. In Deutschland ist der Standort in Augsburg komplett betroffen. (&lt;a href=&quot;https://www.golem.de/specials/fujitsu/&quot;&gt;Fujitsu&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/sap/&quot;&gt;SAP&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137348&amp;amp;page=1&amp;amp;ts=1540562340&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/augsburg-fujitsu-deutschland-macht-alles-dicht/121568,list.html</comments>
<pubDate>Fri, 26 Oct 2018 14:59:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137348-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137348-177485-177484_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Fujitsu will seine gesamte Fertigung außerhalb Japans schließen. In Deutschland ist der Standort in Augsburg komplett betroffen. (<a href="https://www.golem.de/specials/fujitsu/">Fujitsu</a>, <a href="https://www.golem.de/specials/sap/">SAP</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137348&amp;page=1&amp;ts=1540562340" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>56</slash:comments>
</item>
<item>
<title>Bundesnetzagentur: Seehofer fordert Verschiebung von 5G-Auktion</title>
<link>https://www.golem.de/news/bundesnetzagentur-seehofer-fordert-verschiebung-von-5g-auktion-1810-137346-rss.html</link>
<description>Bundesinnenminister Horst Seehofer will die 5G-Auktion verschieben, bis die ländlichen Regionen besser berücksichtigt werden. Er wird von einer Gruppe um den CDU-Abgeordneten Stefan Rouenhoff unterstützt. (&lt;a href=&quot;https://www.golem.de/specials/5g/&quot;&gt;5G&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/bundesnetzagentur/&quot;&gt;Bundesnetzagentur&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137346&amp;amp;page=1&amp;amp;ts=1540557900&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/handy/bundesnetzagentur-seehofer-fordert-verschiebung-von-5g-auktion/121567,list.html</comments>
<pubDate>Fri, 26 Oct 2018 13:45:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137346-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137346-177483-177480_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Bundesinnenminister Horst Seehofer will die 5G-Auktion verschieben, bis die ländlichen Regionen besser berücksichtigt werden. Er wird von einer Gruppe um den CDU-Abgeordneten Stefan Rouenhoff unterstützt. (<a href="https://www.golem.de/specials/5g/">5G</a>, <a href="https://www.golem.de/specials/bundesnetzagentur/">Bundesnetzagentur</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137346&amp;page=1&amp;ts=1540557900" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>14</slash:comments>
</item>
<item>
<title>Linux und Patente: Open Source bei Microsoft ist &quot;Kultur statt Strategie&quot;</title>
<link>https://www.golem.de/news/linux-und-patente-open-source-bei-microsoft-ist-kultur-statt-strategie-1810-137345-rss.html</link>
<description>Der Microsoft-Angestellte Stephen Walli beschreibt den Wandel bei Microsoft hin zu Open Source Software und Linux als kulturell getrieben. Mit Blick auf den Beitritt zu dem Patentpool des Open Invention Network zeigt sich jedoch auch, dass das Unternehmen noch sehr viel Arbeit vor sich hat. Ein Bericht von Sebastian Grüner (&lt;a href=&quot;https://www.golem.de/specials/microsoft/&quot;&gt;Microsoft&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/softwarepatente/&quot;&gt;Softwarepatent&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137345&amp;amp;page=1&amp;amp;ts=1540556820&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/politik-recht/linux-und-patente-open-source-bei-microsoft-ist-kultur-statt-strategie/121566,list.html</comments>
<pubDate>Fri, 26 Oct 2018 13:27:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137345-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137345-177479-177475_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Der Microsoft-Angestellte Stephen Walli beschreibt den Wandel bei Microsoft hin zu Open Source Software und Linux als kulturell getrieben. Mit Blick auf den Beitritt zu dem Patentpool des Open Invention Network zeigt sich jedoch auch, dass das Unternehmen noch sehr viel Arbeit vor sich hat. Ein Bericht von Sebastian Grüner (<a href="https://www.golem.de/specials/microsoft/">Microsoft</a>, <a href="https://www.golem.de/specials/softwarepatente/">Softwarepatent</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137345&amp;page=1&amp;ts=1540556820" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>20</slash:comments>
</item>
<item>
<title>Sicherheitslücke: Daten von 185.000 weiteren British-Airways-Kunden betroffen</title>
<link>https://www.golem.de/news/sicherheitsluecke-daten-von-185-000-weiteren-british-airways-kunden-betroffen-1810-137344-rss.html</link>
<description>Von dem Datenleck im Buchungssystem von British Airways waren deutlich mehr Kunden betroffen als bisher bekannt. Die Fluggesellschaft rät betroffenen Kunden, ihre Bank zu kontaktieren. Kreditkarten werden in diesem Fall häufig komplett ausgetauscht. (&lt;a href=&quot;https://www.golem.de/specials/sicherheitsluecke/&quot;&gt;Sicherheitslücke&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/datenschutz/&quot;&gt;Datenschutz&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137344&amp;amp;page=1&amp;amp;ts=1540553820&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/sicherheitsluecke-daten-von-185.000-weiteren-british-airways-kunden-betroffen/121565,list.html</comments>
<pubDate>Fri, 26 Oct 2018 12:37:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137344-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137344-177474-177471_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Von dem Datenleck im Buchungssystem von British Airways waren deutlich mehr Kunden betroffen als bisher bekannt. Die Fluggesellschaft rät betroffenen Kunden, ihre Bank zu kontaktieren. Kreditkarten werden in diesem Fall häufig komplett ausgetauscht. (<a href="https://www.golem.de/specials/sicherheitsluecke/">Sicherheitslücke</a>, <a href="https://www.golem.de/specials/datenschutz/">Datenschutz</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137344&amp;page=1&amp;ts=1540553820" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments />
</item>
<item>
<title>iPhone Xr im Test: Apples günstigeres iPhone ist nicht günstig</title>
<link>https://www.golem.de/news/iphone-xr-im-test-apples-guenstiges-iphone-ist-nicht-guenstig-1810-137327-rss.html</link>
<description>Apple versucht es 2018 wieder einmal mit einem relativ preisgünstigen iPhone - weniger teuer als die Xs-Modelle, aber mit 850 Euro auch nicht gerade preiswert. Käufer bekommen dafür allerdings auch ein Smartphone mit sehr guter Ausstattung, in einigen Punkten wurde jedoch auf Hardware der teuren Modelle verzichtet. Ein Test von Tobias Költzsch (&lt;a href=&quot;https://www.golem.de/specials/iphone/&quot;&gt;iPhone&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/smartphone/&quot;&gt;Smartphone&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137327&amp;amp;page=1&amp;amp;ts=1540548180&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/handy/iphone-xr-im-test-apples-guenstigeres-iphone-ist-nicht-guenstig/121563,list.html</comments>
<pubDate>Fri, 26 Oct 2018 11:03:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137327-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137327-177418-177414_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Apple versucht es 2018 wieder einmal mit einem relativ preisgünstigen iPhone - weniger teuer als die Xs-Modelle, aber mit 850 Euro auch nicht gerade preiswert. Käufer bekommen dafür allerdings auch ein Smartphone mit sehr guter Ausstattung, in einigen Punkten wurde jedoch auf Hardware der teuren Modelle verzichtet. Ein Test von Tobias Költzsch (<a href="https://www.golem.de/specials/iphone/">iPhone</a>, <a href="https://www.golem.de/specials/smartphone/">Smartphone</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137327&amp;page=1&amp;ts=1540548180" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>169</slash:comments>
</item>
<item>
<title>Microsoft: PC-Spieleangebot des Xbox Game Pass wird erweitert</title>
<link>https://www.golem.de/news/microsoft-pc-spieleangebot-des-xbox-game-pass-wird-erweitert-1810-137343-rss.html</link>
<description>Der Xbox Game Pass soll künftig um mehr Angebote für Windows-PC erweitert werden, sagt Microsoft-Chef Satya Nadella. Derzeit gibt es für 10 Euro nur wenige plattformübergreifend verfügbare Spiele. (&lt;a href=&quot;https://www.golem.de/specials/xbox-one/&quot;&gt;Xbox One&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/microsoft/&quot;&gt;Microsoft&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137343&amp;amp;page=1&amp;amp;ts=1540546800&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/microsoft-pc-spieleangebot-des-xbox-game-pass-wird-erweitert/121562,list.html</comments>
<pubDate>Fri, 26 Oct 2018 10:40:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137343-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137343-177453-177450_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Der Xbox Game Pass soll künftig um mehr Angebote für Windows-PC erweitert werden, sagt Microsoft-Chef Satya Nadella. Derzeit gibt es für 10 Euro nur wenige plattformübergreifend verfügbare Spiele. (<a href="https://www.golem.de/specials/xbox-one/">Xbox One</a>, <a href="https://www.golem.de/specials/microsoft/">Microsoft</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137343&amp;page=1&amp;ts=1540546800" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>19</slash:comments>
</item>
<item>
<title>Breitbandgesellschaft: Grüne wollen Netzbetreiber zum Ausbau zwingen</title>
<link>https://www.golem.de/news/breitbandgesellschaft-gruene-wollen-netzbetreiber-zum-ausbau-zwingen-1810-137342-rss.html</link>
<description>Die Grünen haben die Netzversorgung in Deutschland analysiert. Die Partei, die zurzeit in Wählerumfragen stark zugewinnt, fordert den Breitbandausbau auf Kosten der Konzerne und will Glasfaser staatlich durchsetzen. (&lt;a href=&quot;https://www.golem.de/specials/breitband/&quot;&gt;Breitband&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/handy/&quot;&gt;Handy&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137342&amp;amp;page=1&amp;amp;ts=1540545840&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/politik-recht/breitbandgesellschaft-gruene-wollen-netzbetreiber-zum-ausbau-zwingen/121561,list.html</comments>
<pubDate>Fri, 26 Oct 2018 10:24:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137342-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1807/135605-169300-169297_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die Grünen haben die Netzversorgung in Deutschland analysiert. Die Partei, die zurzeit in Wählerumfragen stark zugewinnt, fordert den Breitbandausbau auf Kosten der Konzerne und will Glasfaser staatlich durchsetzen. (<a href="https://www.golem.de/specials/breitband/">Breitband</a>, <a href="https://www.golem.de/specials/handy/">Handy</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137342&amp;page=1&amp;ts=1540545840" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>22</slash:comments>
</item>
<item>
<title>Studie: Silicon Valley dient als rechte Hand des großen Bruders</title>
<link>https://www.golem.de/news/studie-silicon-valley-dient-als-rechte-hand-des-grossen-bruders-1810-137316-rss.html</link>
<description>Die US-Hightech-Branche verdingt sich zunehmend als technischer Dienstleister für staatliche Big-Brother-Projekte wie die Überwachung und Abschiebung von Immigranten, heißt es in einem Bericht von Bürgerrechtlern. Amazon und Palantir verdienten damit am meisten. Von Stefan Krempl (&lt;a href=&quot;https://www.golem.de/specials/datenschutz/&quot;&gt;Datenschutz&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/ibm/&quot;&gt;IBM&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137316&amp;amp;page=1&amp;amp;ts=1540543080&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/studie-silicon-valley-dient-als-rechte-hand-des-grossen-bruders/121560,list.html</comments>
<pubDate>Fri, 26 Oct 2018 09:38:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137316-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137316-177360-177357_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die US-Hightech-Branche verdingt sich zunehmend als technischer Dienstleister für staatliche Big-Brother-Projekte wie die Überwachung und Abschiebung von Immigranten, heißt es in einem Bericht von Bürgerrechtlern. Amazon und Palantir verdienten damit am meisten. Von Stefan Krempl (<a href="https://www.golem.de/specials/datenschutz/">Datenschutz</a>, <a href="https://www.golem.de/specials/ibm/">IBM</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137316&amp;page=1&amp;ts=1540543080" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>20</slash:comments>
</item>
<item>
<title>Bethesda: Postnukleare PC-Systemanforderungen für Fallout 76</title>
<link>https://www.golem.de/news/bethesda-postnukleare-pc-systemanforderungen-fuer-fallout-76-1810-137340-rss.html</link>
<description>Kurz vor dem Start der Betaversion für PC-Spieler hat Bethesda die Systemanforderung von Fallout 76 veröffentlicht. Hoffnungen auf lange Abenteuer in der Vorabversion gibt es aber zumindest nach den Erfahrungen des Xbox-Zugangs eher nicht. (&lt;a href=&quot;https://www.golem.de/specials/fallout-76/&quot;&gt;Fallout 76&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/rollenspiel/&quot;&gt;Rollenspiel&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137340&amp;amp;page=1&amp;amp;ts=1540542180&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/games/bethesda-postnukleare-pc-systemanforderungen-fuer-fallout-76/121559,list.html</comments>
<pubDate>Fri, 26 Oct 2018 09:23:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137340-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137340-177448-177445_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Kurz vor dem Start der Betaversion für PC-Spieler hat Bethesda die Systemanforderung von Fallout 76 veröffentlicht. Hoffnungen auf lange Abenteuer in der Vorabversion gibt es aber zumindest nach den Erfahrungen des Xbox-Zugangs eher nicht. (<a href="https://www.golem.de/specials/fallout-76/">Fallout 76</a>, <a href="https://www.golem.de/specials/rollenspiel/">Rollenspiel</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137340&amp;page=1&amp;ts=1540542180" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>101</slash:comments>
</item>
<item>
<title>Quartalszahlen: Intel legt 19-Milliarden-USD-Rekord vor</title>
<link>https://www.golem.de/news/quartalszahlen-intel-legt-19-milliarden-usd-rekord-vor-1810-137339-rss.html</link>
<description>Ungeachtet der 14-nm-Knappheit und diverser Sicherheitslücken konnte Intel im dritten Quartal 2018 mehr Umsatz erwirtschaften und mehr Gewinn erzielen als jemals zuvor. Vor allem das florierende Server-Geschäft wird bei Intel immer wichtiger. (&lt;a href=&quot;https://www.golem.de/specials/intel/&quot;&gt;Intel&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/cpu/&quot;&gt;Prozessor&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137339&amp;amp;page=1&amp;amp;ts=1540540260&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/quartalszahlen-intel-legt-19-milliarden-usd-rekord-vor/121558,list.html</comments>
<pubDate>Fri, 26 Oct 2018 08:51:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137339-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137339-177444-177441_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Ungeachtet der 14-nm-Knappheit und diverser Sicherheitslücken konnte Intel im dritten Quartal 2018 mehr Umsatz erwirtschaften und mehr Gewinn erzielen als jemals zuvor. Vor allem das florierende Server-Geschäft wird bei Intel immer wichtiger. (<a href="https://www.golem.de/specials/intel/">Intel</a>, <a href="https://www.golem.de/specials/cpu/">Prozessor</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137339&amp;page=1&amp;ts=1540540260" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>10</slash:comments>
</item>
<item>
<title>Physik: Weg mit der Schönheit!</title>
<link>https://www.golem.de/news/physik-weg-mit-der-schoenheit-1810-137161-rss.html</link>
<description>Ist eine Theorie richtig, nur weil sie schön ist? Nein, sagt Sabine Hossenfelder. In ihrem Buch &quot;Das hässliche Universum&quot; zeigt die theoretische Physikerin, wie das Schönheitsdenken die Wissenschaft lähmt und erklärt dabei recht unterhaltsam die unterschiedlichen Theorien und Modelle der Teilchenphysik. Eine Rezension von Friedemann Zweynert (&lt;a href=&quot;https://www.golem.de/specials/physik/&quot;&gt;Physik&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/internet/&quot;&gt;Internet&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137161&amp;amp;page=1&amp;amp;ts=1540537380&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/internet/physik-weg-mit-der-schoenheit/121557,list.html</comments>
<pubDate>Fri, 26 Oct 2018 08:03:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137161-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137161-176716-176713_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Ist eine Theorie richtig, nur weil sie schön ist? Nein, sagt Sabine Hossenfelder. In ihrem Buch "Das hässliche Universum" zeigt die theoretische Physikerin, wie das Schönheitsdenken die Wissenschaft lähmt und erklärt dabei recht unterhaltsam die unterschiedlichen Theorien und Modelle der Teilchenphysik. Eine Rezension von Friedemann Zweynert (<a href="https://www.golem.de/specials/physik/">Physik</a>, <a href="https://www.golem.de/specials/internet/">Internet</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137161&amp;page=1&amp;ts=1540537380" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>86</slash:comments>
</item>
<item>
<title>Elon Musk: Teslas Model 3 für 35.000 US-Dollar derzeit unmöglich</title>
<link>https://www.golem.de/news/elon-musk-teslas-model-3-fuer-35-000-us-dollar-derzeit-unmoeglich-1810-137335-rss.html</link>
<description>Tesla-Chef Elon Musk hat eingeräumt, das bei 35.000 US-Dollar startende Basismodell des Elektroautos Model 3 immer noch nicht liefern zu können. (&lt;a href=&quot;https://www.golem.de/specials/tesla-model-3/&quot;&gt;Tesla Model 3&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/technologie/&quot;&gt;Technologie&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137335&amp;amp;page=1&amp;amp;ts=1540533540&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/automobil/elon-musk-teslas-model-3-fuer-35.000-us-dollar-derzeit-unmoeglich/121556,list.html</comments>
<pubDate>Fri, 26 Oct 2018 06:59:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137335-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137335-177428-177424_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Tesla-Chef Elon Musk hat eingeräumt, das bei 35.000 US-Dollar startende Basismodell des Elektroautos Model 3 immer noch nicht liefern zu können. (<a href="https://www.golem.de/specials/tesla-model-3/">Tesla Model 3</a>, <a href="https://www.golem.de/specials/technologie/">Technologie</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137335&amp;page=1&amp;ts=1540533540" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>235</slash:comments>
</item>
<item>
<title>Solarzellen als Dach: Tesla-Solarschindeln verzögern sich bis 2019</title>
<link>https://www.golem.de/news/solarzellen-als-dach-tesla-solarschindeln-verzoegern-sich-auf-2019-1810-137334-rss.html</link>
<description>Tesla wird die Serienproduktion seiner Solardachziegel nicht mehr wie geplant in diesem Jahr starten. Nach Angaben von Firmenchef Elon Musk verschiebt sich das Vorhaben auf 2019. (&lt;a href=&quot;https://www.golem.de/specials/solarenergie/&quot;&gt;Solarenergie&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/technologie/&quot;&gt;Technologie&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137334&amp;amp;page=1&amp;amp;ts=1540532460&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wissenschaft/solarzellen-als-dach-tesla-solarschindeln-verzoegern-sich-bis-2019/121555,list.html</comments>
<pubDate>Fri, 26 Oct 2018 06:41:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137334-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1705/127761-139613-i_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Tesla wird die Serienproduktion seiner Solardachziegel nicht mehr wie geplant in diesem Jahr starten. Nach Angaben von Firmenchef Elon Musk verschiebt sich das Vorhaben auf 2019. (<a href="https://www.golem.de/specials/solarenergie/">Solarenergie</a>, <a href="https://www.golem.de/specials/technologie/">Technologie</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137334&amp;page=1&amp;ts=1540532460" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>47</slash:comments>
</item>
<item>
<title>Uniti One: Elektroauto für 15.000 Euro wird in Großbritannien gebaut</title>
<link>https://www.golem.de/news/uniti-one-elektroauto-fuer-15-000-euro-wird-in-grossbritannien-gebaut-1810-137333-rss.html</link>
<description>Das schwedische Unternehmen Uniti will sein Elektroauto One in Großbritannien bauen. Einen fahrenden Prototyp des Uniti One gibt es schon. Das Auto soll je nach Modell für 15.000 bis 20.000 Euro auf den Markt kommen. (&lt;a href=&quot;https://www.golem.de/specials/elektroauto/&quot;&gt;Elektroauto&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/technologie/&quot;&gt;Technologie&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137333&amp;amp;page=1&amp;amp;ts=1540531080&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/automobil/uniti-one-elektroauto-fuer-15.000-euro-wird-in-grossbritannien-gebaut/121554,list.html</comments>
<pubDate>Fri, 26 Oct 2018 06:18:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137333-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1805/134432-163131-163128_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Das schwedische Unternehmen Uniti will sein Elektroauto One in Großbritannien bauen. Einen fahrenden Prototyp des Uniti One gibt es schon. Das Auto soll je nach Modell für 15.000 bis 20.000 Euro auf den Markt kommen. (<a href="https://www.golem.de/specials/elektroauto/">Elektroauto</a>, <a href="https://www.golem.de/specials/technologie/">Technologie</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137333&amp;page=1&amp;ts=1540531080" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>28</slash:comments>
</item>
<item>
<title>Quartalsbericht: Alphabet macht in drei Monaten 9,2 Milliarden Dollar Gewinn</title>
<link>https://www.golem.de/news/quartalsbericht-alphabet-macht-in-drei-monaten-9-2-milliarden-dollar-gewinn-1810-137337-rss.html</link>
<description>Alphabet erwirtschaftet weiter extrem hohe Gewinne, der Umsatz wächst nicht ganz so stark. Google muss zugleich auf eine Enthüllung in der US-Presse zu sexueller Belästigung um Android-Begründer Andy Rubin reagieren. (&lt;a href=&quot;https://www.golem.de/specials/google/&quot;&gt;Google&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/boerse/&quot;&gt;Börse&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137337&amp;amp;page=1&amp;amp;ts=1540504320&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/quartalsbericht-alphabet-macht-in-drei-monaten-9-2-milliarden-dollar-gewinn/121553,list.html</comments>
<pubDate>Thu, 25 Oct 2018 22:52:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137337-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137053-176114-176111_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Alphabet erwirtschaftet weiter extrem hohe Gewinne, der Umsatz wächst nicht ganz so stark. Google muss zugleich auf eine Enthüllung in der US-Presse zu sexueller Belästigung um Android-Begründer Andy Rubin reagieren. (<a href="https://www.golem.de/specials/google/">Google</a>, <a href="https://www.golem.de/specials/boerse/">Börse</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137337&amp;page=1&amp;ts=1540504320" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>12</slash:comments>
</item>
<item>
<title>Quartalsbericht: Amazon verfehlt die Umsatzprognosen</title>
<link>https://www.golem.de/news/quartalsbericht-amazon-verfehlt-die-umsatzprognosen-1810-137336-rss.html</link>
<description>Amazon weist erneut einen hohen Gewinn aus. Doch der Konzern lag beim Umsatz unter den Prognosen der Analysten. (&lt;a href=&quot;https://www.golem.de/specials/amazon/&quot;&gt;Amazon&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/onlineshop/&quot;&gt;Onlineshop&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137336&amp;amp;page=1&amp;amp;ts=1540500780&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/quartalsbericht-amazon-verfehlt-die-umsatzprognosen/121552,list.html</comments>
<pubDate>Thu, 25 Oct 2018 21:53:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137336-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137336-177432-177429_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Amazon weist erneut einen hohen Gewinn aus. Doch der Konzern lag beim Umsatz unter den Prognosen der Analysten. (<a href="https://www.golem.de/specials/amazon/">Amazon</a>, <a href="https://www.golem.de/specials/onlineshop/">Onlineshop</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137336&amp;page=1&amp;ts=1540500780" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>24</slash:comments>
</item>
<item>
<title>Datenskandal: Britische Datenschutzbehörde verurteilt Facebook</title>
<link>https://www.golem.de/news/datenskandal-britische-datenschutzbehoerde-verurteilt-facebook-1810-137332-rss.html</link>
<description>Im Skandal um Cambridge Analytica hat die britische Datenschutzbehörde die Höchststrafe von 500.000 Pfund verhängt. Facebook habe einen schweren Verstoß gegen geltendes Recht zugelassen. (&lt;a href=&quot;https://www.golem.de/specials/facebook/&quot;&gt;Facebook&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/socialnetwork/&quot;&gt;Soziales Netz&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137332&amp;amp;page=1&amp;amp;ts=1540483080&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/datenskandal-britische-datenschutzbehoerde-verurteilt-facebook/121550,list.html</comments>
<pubDate>Thu, 25 Oct 2018 16:58:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137332-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137332-177420-177419_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Im Skandal um Cambridge Analytica hat die britische Datenschutzbehörde die Höchststrafe von 500.000 Pfund verhängt. Facebook habe einen schweren Verstoß gegen geltendes Recht zugelassen. (<a href="https://www.golem.de/specials/facebook/">Facebook</a>, <a href="https://www.golem.de/specials/socialnetwork/">Soziales Netz</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137332&amp;page=1&amp;ts=1540483080" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>35</slash:comments>
</item>
<item>
<title>Corsair: Neue K70 MK.2 kommt mit Cherrys Low-Profile-Switches</title>
<link>https://www.golem.de/news/corsair-neue-k70-mk-2-kommt-mit-cherrys-low-profile-switches-1810-137331-rss.html</link>
<description>Corsair erweitert sein Tastaturportefeuille um zwei Gaming-Tastaturen mit Cherrys flachen Low-Profile-Switches. Ein Modell hat Schalter mit einem besonders kurzem Auslöseweg von 1 mm - die Schalter darf Corsair exklusiv verwenden. (&lt;a href=&quot;https://www.golem.de/specials/corsair/&quot;&gt;Corsair&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/eingabegeraet/&quot;&gt;Eingabegerät&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137331&amp;amp;page=1&amp;amp;ts=1540480320&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/sonstiges/corsair-neue-k70-mk.2-kommt-mit-cherrys-low-profile-switches/121549,list.html</comments>
<pubDate>Thu, 25 Oct 2018 16:12:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137331-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137331-177413-177410_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Corsair erweitert sein Tastaturportefeuille um zwei Gaming-Tastaturen mit Cherrys flachen Low-Profile-Switches. Ein Modell hat Schalter mit einem besonders kurzem Auslöseweg von 1 mm - die Schalter darf Corsair exklusiv verwenden. (<a href="https://www.golem.de/specials/corsair/">Corsair</a>, <a href="https://www.golem.de/specials/eingabegeraet/">Eingabegerät</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137331&amp;page=1&amp;ts=1540480320" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>19</slash:comments>
</item>
<item>
<title>Cambridge-Analytica-Skandal: EU-Parlament fordert schärfere Kontrolle von Facebook</title>
<link>https://www.golem.de/news/cambridge-analytica-skandal-eu-parlament-fordert-schaerfere-kontrolle-von-facebook-1810-137329-rss.html</link>
<description>Die EU-Abgeordneten haben als Reaktion auf die Datenschutzverstöße von Facebook und Cambridge Analytica Behörden angehalten, ihre Aktivitäten auf dem Netzwerk zu überdenken. Profiling zu politischen Zwecken wollen sie verbieten. Von Stefan Krempl (&lt;a href=&quot;https://www.golem.de/specials/facebook/&quot;&gt;Facebook&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/socialnetwork/&quot;&gt;Soziales Netz&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137329&amp;amp;page=1&amp;amp;ts=1540479120&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/cambridge-analytica-skandal-eu-parlament-fordert-schaerfere-kontrolle-von-facebook/121548,list.html</comments>
<pubDate>Thu, 25 Oct 2018 15:52:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137329-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137329-177404-177403_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die EU-Abgeordneten haben als Reaktion auf die Datenschutzverstöße von Facebook und Cambridge Analytica Behörden angehalten, ihre Aktivitäten auf dem Netzwerk zu überdenken. Profiling zu politischen Zwecken wollen sie verbieten. Von Stefan Krempl (<a href="https://www.golem.de/specials/facebook/">Facebook</a>, <a href="https://www.golem.de/specials/socialnetwork/">Soziales Netz</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137329&amp;page=1&amp;ts=1540479120" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>3</slash:comments>
</item>
<item>
<title>Kuriosum: Das Hellgate London öffnet sich mal wieder</title>
<link>https://www.golem.de/news/kuriosum-das-hellgate-london-oeffnet-sich-mal-wieder-1810-137328-rss.html</link>
<description>Einer der großen Trash-Klassiker der Spielegeschichte wagt einen neuen Anlauf: Mitte November 2018 soll ein neue, für Einzelspieler ausgelegte Windows-Fassung von Hellgate London erscheinen. Das Ursprungskonzept hatten sich ehemalige Blizzard-Chefentwickler ausgedacht. (&lt;a href=&quot;https://www.golem.de/specials/rollenspiel/&quot;&gt;Rollenspiel&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/steam/&quot;&gt;Steam&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137328&amp;amp;page=1&amp;amp;ts=1540476600&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/games/kuriosum-das-hellgate-london-oeffnet-sich-mal-wieder/121547,list.html</comments>
<pubDate>Thu, 25 Oct 2018 15:10:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137328-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137328-177396-177392_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Einer der großen Trash-Klassiker der Spielegeschichte wagt einen neuen Anlauf: Mitte November 2018 soll ein neue, für Einzelspieler ausgelegte Windows-Fassung von Hellgate London erscheinen. Das Ursprungskonzept hatten sich ehemalige Blizzard-Chefentwickler ausgedacht. (<a href="https://www.golem.de/specials/rollenspiel/">Rollenspiel</a>, <a href="https://www.golem.de/specials/steam/">Steam</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137328&amp;page=1&amp;ts=1540476600" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>61</slash:comments>
</item>
<item>
<title>Tweether: 10 GBit/s über einen Quadratkilometer verteilt</title>
<link>https://www.golem.de/news/tweether-10-gbit-s-ueber-einen-quadratkilometer-verteilt-1810-137326-rss.html</link>
<description>Eine neue Technologie verteilt 10 GBit/s über eine große Fläche. Der erste Feldversuch ist erfolgreich verlaufen. Zum ersten Mal wurde ein stabiles drahtloses Netzwerk bei diesen Frequenzen und mit diesen Datenraten betrieben. (&lt;a href=&quot;https://www.golem.de/specials/wissenschaft/&quot;&gt;Wissenschaft&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/mobil/&quot;&gt;Mobil&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137326&amp;amp;page=1&amp;amp;ts=1540475400&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wissenschaft/tweether-10-gbit-s-ueber-einen-quadratkilometer-verteilt/121546,list.html</comments>
<pubDate>Thu, 25 Oct 2018 14:50:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137326-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137326-177391-177388_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Eine neue Technologie verteilt 10 GBit/s über eine große Fläche. Der erste Feldversuch ist erfolgreich verlaufen. Zum ersten Mal wurde ein stabiles drahtloses Netzwerk bei diesen Frequenzen und mit diesen Datenraten betrieben. (<a href="https://www.golem.de/specials/wissenschaft/">Wissenschaft</a>, <a href="https://www.golem.de/specials/mobil/">Mobil</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137326&amp;page=1&amp;ts=1540475400" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>20</slash:comments>
</item>
<item>
<title>Red Dead Redemption 2: Saloon-Prügelei (der Worte) im Livestream</title>
<link>https://www.golem.de/news/red-dead-redemption-2-saloon-pruegelei-der-worte-im-livestream-1810-137312-rss.html</link>
<description> Die Golem.de-Redakteure Peter Steinlechner und Michael Wieczorek diskutieren gemeinsam mit unserer Community über den Test zu Red Dead Redemption 2 live ab 18 Uhr. (&lt;a href=&quot;https://www.golem.de/specials/red-dead-redemption-2/&quot;&gt;Red Dead Redemption 2&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/spieletest/&quot;&gt;Spieletest&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137312&amp;amp;page=1&amp;amp;ts=1540474200&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/games/red-dead-redemption-2-saloon-pruegelei-der-worte-im-livestream/121545,list.html</comments>
<pubDate>Thu, 25 Oct 2018 14:30:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137312-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137312-177341-177338_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left"> Die Golem.de-Redakteure Peter Steinlechner und Michael Wieczorek diskutieren gemeinsam mit unserer Community über den Test zu Red Dead Redemption 2 live ab 18 Uhr. (<a href="https://www.golem.de/specials/red-dead-redemption-2/">Red Dead Redemption 2</a>, <a href="https://www.golem.de/specials/spieletest/">Spieletest</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137312&amp;page=1&amp;ts=1540474200" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments />
</item>
<item>
<title>Wolf Intelligence: Trojanerfirma aus Deutschland lässt interne Daten im Netz</title>
<link>https://www.golem.de/news/wolf-intelligence-trojanerfirma-aus-deutschland-laesst-interne-daten-im-netz-1810-137323-rss.html</link>
<description>Wolf Intelligence verkauft Schadsoftware an Staaten. Eine Sicherheitsfirma hat sensible Daten des Unternehmens öffentlich zugänglich im Internet gefunden. In einer Präsentation wurden die Funde gezeigt. (&lt;a href=&quot;https://www.golem.de/specials/trojaner/&quot;&gt;Trojaner&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/virus/&quot;&gt;Virus&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137323&amp;amp;page=1&amp;amp;ts=1540472400&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/wolf-intelligence-trojanerfirma-aus-deutschland-laesst-interne-daten-im-netz/121543,list.html</comments>
<pubDate>Thu, 25 Oct 2018 14:00:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137323-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137323-177383-177380_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Wolf Intelligence verkauft Schadsoftware an Staaten. Eine Sicherheitsfirma hat sensible Daten des Unternehmens öffentlich zugänglich im Internet gefunden. In einer Präsentation wurden die Funde gezeigt. (<a href="https://www.golem.de/specials/trojaner/">Trojaner</a>, <a href="https://www.golem.de/specials/virus/">Virus</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137323&amp;page=1&amp;ts=1540472400" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>16</slash:comments>
</item>
<item>
<title>NBN: Der Top-Nutzer verwendet 24 TByte im Monat</title>
<link>https://www.golem.de/news/nbn-der-top-nutzer-verwendet-24-tbyte-im-monat-1810-137324-rss.html</link>
<description> Ein staatliches FTTH-Netzwerk für fast alle in Australien bis 2017 war einst das Ziel. Doch davon ist beim (NBN) National Broadband Network nicht mehr viel übrig geblieben. In Berlin wurde eine Zwischenbilanz gezogen. (&lt;a href=&quot;https://www.golem.de/specials/festnetz/&quot;&gt;Festnetz&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/dsl/&quot;&gt;DSL&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137324&amp;amp;page=1&amp;amp;ts=1540471380&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/internet/nbn-der-top-nutzer-verwendet-24-tbyte-im-monat/121542,list.html</comments>
<pubDate>Thu, 25 Oct 2018 13:43:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137324-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137324-177387-177384_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left"> Ein staatliches FTTH-Netzwerk für fast alle in Australien bis 2017 war einst das Ziel. Doch davon ist beim (NBN) National Broadband Network nicht mehr viel übrig geblieben. In Berlin wurde eine Zwischenbilanz gezogen. (<a href="https://www.golem.de/specials/festnetz/">Festnetz</a>, <a href="https://www.golem.de/specials/dsl/">DSL</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137324&amp;page=1&amp;ts=1540471380" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>51</slash:comments>
</item>
<item>
<title>Linux-Kernel: Mit Machine Learning auf der Suche nach Bug-Fixes</title>
<link>https://www.golem.de/news/linux-kernel-mit-machine-learning-auf-der-suche-nach-bug-fixes-1810-137321-rss.html</link>
<description>Wichtige Patches, die in stabilen Kernel-Versionen landen sollten, werden von der Linux-Community oft vergessen oder übersehen. Abhilfe schaffen soll offenbar Machine Learning, wie die Entwickler Sasha Levin und Julia Lawall erklären. (&lt;a href=&quot;https://www.golem.de/specials/linux-kernel/&quot;&gt;Linux-Kernel&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/linux/&quot;&gt;Linux&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137321&amp;amp;page=1&amp;amp;ts=1540468800&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/security/linux-kernel-mit-machine-learning-auf-der-suche-nach-bug-fixes/121541,list.html</comments>
<pubDate>Thu, 25 Oct 2018 13:00:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137321-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137321-177375-177372_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Wichtige Patches, die in stabilen Kernel-Versionen landen sollten, werden von der Linux-Community oft vergessen oder übersehen. Abhilfe schaffen soll offenbar Machine Learning, wie die Entwickler Sasha Levin und Julia Lawall erklären. (<a href="https://www.golem.de/specials/linux-kernel/">Linux-Kernel</a>, <a href="https://www.golem.de/specials/linux/">Linux</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137321&amp;page=1&amp;ts=1540468800" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>4</slash:comments>
</item>
<item>
<title>Quartalszahlen: AMDs Aktie gibt wegen mäßiger Aussichten nach</title>
<link>https://www.golem.de/news/quartalszahlen-amds-aktie-gibt-wegen-maessiger-aussichten-nach-1810-137320-rss.html</link>
<description>Im dritten Quartal 2018 konnte AMD zwar Umsatz und Gewinn steigern, aber nicht so stark wie erwartet. Die Aktie brach dennoch von über 25 US-Dollar auf 17 US-Dollar ein, da das vierte Quartal schlechter laufen wird als von den Anlegern gedacht - hier wurde zu viel erwartet. (&lt;a href=&quot;https://www.golem.de/specials/amd/&quot;&gt;AMD&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/cpu/&quot;&gt;Prozessor&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137320&amp;amp;page=1&amp;amp;ts=1540467600&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/wirtschaft/quartalszahlen-amds-aktie-gibt-wegen-maessiger-aussichten-nach/121540,list.html</comments>
<pubDate>Thu, 25 Oct 2018 12:40:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137320-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137320-177379-177376_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Im dritten Quartal 2018 konnte AMD zwar Umsatz und Gewinn steigern, aber nicht so stark wie erwartet. Die Aktie brach dennoch von über 25 US-Dollar auf 17 US-Dollar ein, da das vierte Quartal schlechter laufen wird als von den Anlegern gedacht - hier wurde zu viel erwartet. (<a href="https://www.golem.de/specials/amd/">AMD</a>, <a href="https://www.golem.de/specials/cpu/">Prozessor</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137320&amp;page=1&amp;ts=1540467600" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>17</slash:comments>
</item>
<item>
<title>Projekt Broadband: Bahn will 3,5 Milliarden Euro vom Bund für Glasfasernetz</title>
<link>https://www.golem.de/news/projekt-broadband-bahn-will-3-5-milliarden-euro-vom-bund-fuer-glasfasernetz-1810-137322-rss.html</link>
<description>Die Pläne für das eigene Glasfasernetz der Deutschen Bahn werden konkret. Über die Finanzierung redet man jetzt mit der Regierung. Das Netz soll schnell gebaut werden. (&lt;a href=&quot;https://www.golem.de/specials/deutsche-bahn/&quot;&gt;Deutsche Bahn&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/umts/&quot;&gt;UMTS&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137322&amp;amp;page=1&amp;amp;ts=1540466580&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/internet/projekt-broadband-bahn-will-3-5-milliarden-euro-vom-bund-fuer-glasfasernetz/121539,list.html</comments>
<pubDate>Thu, 25 Oct 2018 12:23:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137322-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1808/136062-171340-171337_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Die Pläne für das eigene Glasfasernetz der Deutschen Bahn werden konkret. Über die Finanzierung redet man jetzt mit der Regierung. Das Netz soll schnell gebaut werden. (<a href="https://www.golem.de/specials/deutsche-bahn/">Deutsche Bahn</a>, <a href="https://www.golem.de/specials/umts/">UMTS</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137322&amp;page=1&amp;ts=1540466580" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>31</slash:comments>
</item>
<item>
<title>Red Dead Redemption 2 im Test: Der Revolverhelden-Simulator</title>
<link>https://www.golem.de/news/red-dead-redemption-2-im-test-der-revolverhelden-simulator-1810-137304-rss.html</link>
<description>Reiten, prügeln, kochen, jagen, schießen, böse sein oder (relativ) brav: In Red Dead Redemption 2 gibt es enorme Möglichkeiten, sich als Revolverheld in einer wunderschönen Westernwelt auszuleben. Das Actionspiel von Rockstar Games ist ein großer Spaß - aber nicht ganz so gut wie GTA 5. Von Peter Steinlechner (&lt;a href=&quot;https://www.golem.de/specials/red-dead-redemption-2/&quot;&gt;Red Dead Redemption 2&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/spieletest/&quot;&gt;Spieletest&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137304&amp;amp;page=1&amp;amp;ts=1540465260&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/games/red-dead-redemption-2-im-test-der-revolverhelden-simulator/121537,list.html</comments>
<pubDate>Thu, 25 Oct 2018 12:01:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137304-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137304-177303-177300_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Reiten, prügeln, kochen, jagen, schießen, böse sein oder (relativ) brav: In Red Dead Redemption 2 gibt es enorme Möglichkeiten, sich als Revolverheld in einer wunderschönen Westernwelt auszuleben. Das Actionspiel von Rockstar Games ist ein großer Spaß - aber nicht ganz so gut wie GTA 5. Von Peter Steinlechner (<a href="https://www.golem.de/specials/red-dead-redemption-2/">Red Dead Redemption 2</a>, <a href="https://www.golem.de/specials/spieletest/">Spieletest</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137304&amp;page=1&amp;ts=1540465260" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>107</slash:comments>
</item>
<item>
<title>Xiaomi: Das Mi Mix 3 hat keine Notch und eine versteckte Frontkamera</title>
<link>https://www.golem.de/news/xiaomi-das-mi-mix-3-hat-keine-notch-und-eine-versteckte-frontkamera-1810-137319-rss.html</link>
<description>Das Display von Xiaomis angekündigtem Smartphone Mi Mix 3 ist nahezu randlos. Die Frontkamera versteckt das Gerät hinter der aufschiebbaren Schale. Neu ist zudem, dass Xiaomi ein OLED-Panel verbaut und wieder den Qi-Ladestandard nutzt. (&lt;a href=&quot;https://www.golem.de/specials/xiaomi/&quot;&gt;Xiaomi&lt;/a&gt;, &lt;a href=&quot;https://www.golem.de/specials/smartphone/&quot;&gt;Smartphone&lt;/a&gt;) &lt;img src=&quot;https://cpx.golem.de/cpx.php?class=17&amp;amp;aid=137319&amp;amp;page=1&amp;amp;ts=1540464540&quot; alt=&quot;&quot; width=&quot;1&quot; height=&quot;1&quot; /&gt;</description>
<comments>https://forum.golem.de/kommentare/handy/xiaomi-das-mi-mix-3-hat-keine-notch-und-eine-versteckte-frontkamera/121536,list.html</comments>
<pubDate>Thu, 25 Oct 2018 11:49:00 +0100</pubDate>
<guid>https://www.golem.de/1810/137319-rss.html</guid>
<content:encoded><![CDATA[<img src="https://www.golem.de/1810/137319-177371-177370_rc.jpg" width="140" height="140" vspace="3" hspace="8" align="left">Das Display von Xiaomis angekündigtem Smartphone Mi Mix 3 ist nahezu randlos. Die Frontkamera versteckt das Gerät hinter der aufschiebbaren Schale. Neu ist zudem, dass Xiaomi ein OLED-Panel verbaut und wieder den Qi-Ladestandard nutzt. (<a href="https://www.golem.de/specials/xiaomi/">Xiaomi</a>, <a href="https://www.golem.de/specials/smartphone/">Smartphone</a>) <img src="https://cpx.golem.de/cpx.php?class=17&amp;aid=137319&amp;page=1&amp;ts=1540464540" alt="" width="1" height="1" />]]></content:encoded>
<slash:comments>125</slash:comments>
</item>
</channel>
</rss>

View File

@ -1,79 +0,0 @@
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<channel>
<title>Flux RSS du magazine de psychologie Le Cercle Psy</title>
<link>https://le-cercle-psy.scienceshumaines.com/rss</link>
<description>Flux RSS du magazine de psychologie Le Cercle Psy, le magazine de toutes les psychologies.</description>
<copyright>Le Cercle Psy</copyright>
<item>
<title>Perturbateurs endocriniens : quels effets sur le cerveau ?</title>
<link>https://le-cercle-psy.scienceshumaines.com/perturbateurs-endocriniens-quels-effets-sur-le-cerveau_sh_39995</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Si leur impact semble discret au premier abord, nombre d'études montrent que les perturbateurs endocriniens pourraient être à l'origine de troubles neuro-développementaux chez l'enfant.</description>
</item>
<item>
<title>Masters en Psycho : une simplificationet#8230; très complexe </title>
<link>https://le-cercle-psy.scienceshumaines.com/masters-en-psycho-une-simplification-tres-complexe_sh_40065</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Une nouvelle nomenclature adoptée en 2014 a voulu simplifier les options proposées aux étudiants en Master de Psychologie. Mais on en revient à des choix aussi illisibles qu'auparavant !</description>
</item>
<item>
<title>La criminalité liée surtout à... l'ennui ?</title>
<link>https://le-cercle-psy.scienceshumaines.com/la-criminalite-liee-surtout-a-l-ennui_sh_39986</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>« L'oisiveté est mère de tous les vices », dit le proverbe... Certains chercheurs américains paraissent proches de cette position !</description>
</item>
<item>
<title></title>
<link></link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description></description>
</item>
<item>
<title>Caroline Eliacheff : « Dolto reste authentiquement subversive »</title>
<link>https://le-cercle-psy.scienceshumaines.com/caroline-eliacheff-dolto-reste-authentiquement-subversive_sh_39992</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Françoise Dolto est morte il y a trente ans. D'abord adulée par des générations de parents et de collègues, on lui a ensuite reproché d'avoir favorisé l'émergence d'enfants-rois tyranniques. Et s'il existait une autre voie ?</description>
</item>
<item>
<title>L'enfant doué : quand trop comprendre... empêche parfois de comprendre</title>
<link>https://le-cercle-psy.scienceshumaines.com/l-enfant-doue-quand-trop-comprendre-empeche-parfois-de-comprendre_sh_40004</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>On ne le répètera jamais assez : réaliser le portrait-robot d'un enfant « doué », « surdoué », « à haut potentiel », peu importe la qualification choisie, est vain. Cet ouvrage nous rappelle que chaque facilité, talent, compétence ou même don, peut s'accompagner d'un versant potentiellement plus problématique. Mais insistons : potentiellement.</description>
</item>
<item>
<title>Travail, organisations, emploi : les modèles européens</title>
<link>https://le-cercle-psy.scienceshumaines.com/travail-organisations-emploi-les-modeles-europeens_sh_33090</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Les pays européens diffèrent en matière de performance, de niveau de chômage et de qualité de vie au travail. Certains pays réussissent mieux que d'autres et sont pris comme «modèles». Comment font-ils?</description>
</item>
<item>
<title>Migrants : l'urgence thérapeutique</title>
<link>https://le-cercle-psy.scienceshumaines.com/migrants-l-urgence-therapeutique_sh_39180</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Poussés à l'exil par les conflits, la pauvreté et l'espoir d'une vie meilleure, les migrants arrivent après un long parcours. Beaucoup sont blessés, brisés, désespérés parfois. Quelle réponse pour les aider ?</description>
</item>
<item>
<title>Psy en prison, une mission impossible ?</title>
<link>https://le-cercle-psy.scienceshumaines.com/psy-en-prison-une-mission-impossible_sh_38718</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Détenus proches de la psychose, manque cruel de moyens, hiérarchie intrusive... Les praticiens intervenant en prison n'ont pas un quotidien facile. Retour sur un sacerdoce des temps modernes.</description>
</item>
<item>
<title>Psychologue à domicile : de la clinique à l'état brut</title>
<link>https://le-cercle-psy.scienceshumaines.com/psychologue-a-domicile-de-la-clinique-a-l-etat-brut_sh_35540</link>
<pubDate>Wed, 17 Oct 2018 10:30:00 GMT</pubDate>
<description>Si tu ne peux pas venir au psychologue, le psychologue viendra à toi ! L'intervention à domicile demeure une pratique encore peu répandue chez les psys. En quoi diffère-t-elle d'une consultation ordinaire ?</description>
</item>
</channel>
</rss>

View File

@ -1,862 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>警惕7大最常见的网络钓鱼主题行看看你中招没 - 51CTO.COM</title>
<meta name="description" content="如今,我们的电子邮件中正潜伏着各种网络钓鱼骗局。即便是你现在没有发现,那么也许明天或者是后天……总之总有一天会发现。关键问题是,在面对网络钓鱼骗局时,你会中招吗?"/>
<meta name="keywords" content="网络钓鱼,攻击,网络安全"/>
<base target="_blank" />
<link rel="stylesheet" type="text/css" href="https://static4.51cto.com/51cto/cms/2016/css/article_head.css?v=0.1"/>
<link rel="stylesheet" type="text/css" href="https://static1.51cto.com/51cto/cms/2016/css/article_layout.css?v=2.17"/>
<link rel="stylesheet" type="text/css" href="https://static2.51cto.com/51cto/cms/2016/css/article_right.css"/>
<script type="text/javascript" src="https://static1.51cto.com/libs/jquery/1.8.3/jquery.min.js"></script>
</head>
<body >
<!-- 导航 开始-->
<!-- top start -->
<style>
.home-top .subweb .sub_widthAuto{width:auto;}
.top-nav .nav li .topnavA_zxf{width:194px;left:-60px;}
.top-nav .topnavA_zxf a{padding: 0 10px;}
.top-nav .nav li:hover .topnavA_zxf a{width:auto;text-indent:inherit;}
.zxf_menubot{background:#F4F4F4;padding:15px 20px 0;border-top:4px solid #be0000;font-size:14px;}
.zxf_menubot a{padding:0;}
.zxf_menubot dl{margin-right:48px;_margin-right:40px;*margin-right:40px;}
.zxf_menubot .nmr{margin-right:0;}
.zxf_menubot dt,.zxf_menubot dd{float:left;}
.zxf_menulist li{float:left;text-align:left;margin-right:20px;}
.zxf_menulist a{display:block;margin-bottom:10px;}
.zxf_menulist li.nmr{margin-right:0;}
.zxf_menu_ico{width:28px;margin-right:20px;text-align:center;color:#666;}
.zxf_menu_ico img{display:block;margin:10px auto 10px;}
.redNav{color:#be0000;}
.subweb-list .navcodebox{ width:410px; left:-225px; padding:20px 15px;}
.subweb-list .navcodebox span{ width:136px;display:inline-block; float:left; text-align:center; color:#747474; font-size:13px; }
.subweb-list .navcodebox span p{ padding-top: 10px;color:#333;}
.rmzw_xx li a {
float: left;
font-size: 16px;
height: 23px;
line-height: 23px;
overflow: hidden;
width: 95%;
}
.rmzw_xx li span.ico {
background: rgba(0, 0, 0, 0) url("https://s2.51cto.com/wyfs02/M02/98/D8/wKiom1lBBebwbP2OAAAAuOBywqs269.png") no-repeat scroll left center;
float: left;
height: 22px;
line-height: 22px;
width: 15px;
}
.rmzw_xx li {
border-bottom: 1px dotted #ccc;
float: left;
margin-top: 6px;
padding: 5px 0;
width: 100%;
}
.ctoll1 dd a {
color: #343434;
float: left;
font-family: "Microsoft YaHei";
font-size: 14px;
padding: 15px 19px 0;
}
</style>
<div class="home-top" id="topx">
<div class="w1001 cent">
<div class="pdr10 fl"><a href="http://www.51cto.com/">51CTO首页</a></div>
<div class="pdr10 fl">|</div>
<div class="pdr10 fl">
<div class="subweb"><span class="trans">技术频道</span><i></i>
<div class="subweb-list" style="width: 90px; top: 37px;">
<a href="http://ai.51cto.com/" target="_blank">人工智能</a>
<a href="http://cloud.51cto.com/" target="_blank">云计算</a>
<a href="http://bigdata.51cto.com/" target="_blank">大数据</a>
<a href="http://network.51cto.com/" target="_blank">网络</a>
<a href="http://developer.51cto.com/" target="_blank">开发</a>
<a href="http://netsecurity.51cto.com/" target="_blank">安全</a>
<a href="http://os.51cto.com/" target="_blank">系统</a>
<a href="http://mobile.51cto.com/" target="_blank">移动</a>
<a href="http://server.51cto.com/" target="_blank">服务器</a>
</div>
</div>
</div>
<div class="pdr10 fl">|</div>
<div class="pdr10 fl">
<div class="subweb"><span class="trans">51CTO旗下网站</span><i></i>
<!--旗下网站-->
<div class="subweb-list" style="top: 37px;">
<a href="http://www.51cto.com" target="_blank">51CTO.COM</a>
<a href="http://www.cioage.com" target="_blank">CIOAge.COM</a>
<a href="http://www.hc3i.cn" target="_blank">HC3i.CN</a>
<!--<a href="http://zhijiapro.com/" target="_blank">zhijiapro.com</a>-->
</div>
<!--旗下网站-->
</div>
</div>
<div class="pdr10 fl">|</div>
<div class="pdr10 fl"><a href="http://www.51cto.com/about/map.htm" target="_blank">地图</a></div>
<div class="pdr10 fl">|</div>
<div class="pdr10 fl">
<div class="subweb"><span class="trans">移动端</span><i></i>
<!--旗下网站-->
<div class="subweb-list sub_widthAuto" style="top: 37px;">
<div class="navcodebox clearfix" style="width: 546px; display: block;top: 0; border-top: 0; left:-1px;">
<span>
<img src="https://s3.51cto.com/wyfs02/M02/97/8A/wKiom1kvkpKBgQFIAACDBen2-dg668.jpg" width="105" height="107" />
<p>51CTO技术栈</p>
</span>
<span>
<img src="http://s5.51cto.com/wyfs02/M02/8E/33/wKioL1i40HrAObC6AAAcvPVCCV0975.jpg" width="105" height="107" />
<p>51CTO微站服务号</p>
</span>
<span>
<img src="http://s3.51cto.com/wyfs02/M00/8E/33/wKioL1i40HrQZy_TAAAcDOkrRAE327.jpg" width="107" height="107" />
<p>51CTO学院客户端</p>
</span>
<span>
<img src="https://s3.51cto.com/oss/201712/27/48594addc4b5eebf52a15bd51262ab23.jpg" width="107" height="107" />
<p>CIO进化论</p>
</span>
</div>
</div>
<!--旗下网站-->
</div>
</div>
<div class="top-r">
<div id="login_status" style="text-align:right;" class="login"> </div>
</div>
</div>
</div>
<!-- top end -->
<!-- 导航 结束-->
<!-- 频道导航 -->
<div class="top_bg">
<div class="wrap">
<div id="tonglanad" class="left"></div>
<div id="list4" class="right" style="position:relative;">
<ul>
<li id="wordlinkad1"></li>
<li id="wordlinkad2"></li>
<li id="wordlinkad3"></li>
<li id="wordlinkad4"></li>
</ul>
<div style="right: 0px; width: 24px; height: 14px; z-index: 12; position: absolute; background: transparent url('http://s5.51cto.com/wyfs02/M00/86/BB/wKiom1fI4nWStYqXAAAEoZQn6vs942.png') repeat scroll 0% 0%; bottom: 2px;"></div>
</div>
</div>
<div class="nav">
<a href="http://www.51cto.com" class="logo"><img src="http://static4.51cto.com/51cto/cms/2016/images/nr_logo.png?v=0.1" alt=""></a>
<ul>
<li><a href="http://netsecurity.51cto.com" class="active">安全频道</a></li>
<li><a href="http://netsecurity.51cto.com">首页</a></li>
<li><a href="http://netsecurity.51cto.com/col/1073/">资讯</a></li>
<li><a href="http://netsecurity.51cto.com/col/516/">应用安全</a></li>
<li><a href="http://netsecurity.51cto.com/col/1068/">数据安全</a></li>
<li><a href="http://netsecurity.51cto.com/col/1537/">移动安全</a></li>
<li><a href="http://netsecurity.51cto.com/col/1591/">云安全</a></li>
<li><a href="http://netsecurity.51cto.com/col/518/">黑客</a></li>
<li><a href="http://netsecurity.51cto.com/col/1593/">工具</a></li>
<li><a href="http://netsecurity.51cto.com/speclist/1156/">专题</a></li>
</ul>
<div class="nav-rsear">
<form method="post" action="http://www.51cto.com/php/search.php" name="searchform" target="_blank">
<input name="keyword" id="q" type="text" placeholder="输入您要搜索的内容" class="sear-1">
<input name="" type="submit" value="" class="sear-2">
</form>
</div>
</div>
</div>
<!-- 频道导航结束 -->
<div class="main">
<!-- 左侧内容 -->
<div class="main_left">
<div class="wznr">
<h2>警惕7大最常见的网络钓鱼主题行看看你中招没</h2>
<p>如今,我们的电子邮件中正潜伏着各种网络钓鱼骗局。即便是你现在没有发现,那么也许明天或者是后天……总之总有一天会发现。关键问题是,在面对网络钓鱼骗局时,你会中招吗?</p>
<dl>
<dt><span>作者:小二郎</span><span>来源:<a href='http://www.4hou.com/info/news/14195.html' target='_blank'>4hou</a></span>|<em>2018-10-29 10:35</em></dt>
<dd>
<div class="left" style="padding-right: 10px">
<a href="javascript:favorBox('open');" title="一键收藏,随时查看,分享好友!" target="_self" class="bds_more1">&nbsp;收藏</a>
</div>
<div class="bdsharebuttonbox left" data-tag="share_2">
<a href="javascript:;" class="bds_more" data-cmd="more">&nbsp;&nbsp;分享</a>
</div>
</dd>
</dl>
</div>
<div class="zwnr">
<!-- <h2><a href="http://mdsa.51cto.com/act/Tech/Tech24" target="_blank" style="text-decoration:none;">【51CTO技术沙龙】10月27日让我们共同探索AI场景化应用实现之道</a></h2> -->
<p>如今,我们的电子邮件中正潜伏着各种网络钓鱼骗局。即便是你现在没有发现,那么也许明天或者是后天&hellip;&hellip;总之总有一天会发现。关键问题是,在面对网络钓鱼骗局时,你会中招吗?</p>
<p>有研究人员发表的报告声明电子邮件中的网络钓鱼骗局已经变得越来越难以阻止攻击者正在创新和完善他们的诱饵使网络钓鱼更逼真从而对用户更具吸引力和说服力。Webroot公司首席信息安全官Gary Hayslip表示虽然用户越来越警惕各种钓鱼诈骗的攻击各种防网络钓鱼软件也不少但还是不能阻挡各种各样的钓鱼诈骗攻击。正所谓&ldquo;道高一尺魔高一丈&rdquo;,网络钓鱼攻击依旧通过各种方式,来骗取个人用户和企业的信息。</p>
<p>Hayslip表示</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;我觉得电子邮件中的网络钓鱼骗局已经到了普遍存在的地步。用户现在也习惯下意识地点击查看网络钓鱼电子邮件,即便他们明知不应该这么做。这就是攻击者利用人性来实施攻击活动的可怕之处!&rdquo;</td>
</tr>
</tbody>
</table>
<p>人们总是充满好奇心和同情心,渴望通过自己的力量来帮助有需要的人,而这两种品质正是他们易受网络钓鱼攻击的根源所在。当他们中招了以后,他们又习惯用&ldquo;我当时忙糊涂了&rdquo;&ldquo;我忘了&rdquo;&ldquo;我本应该知道这是钓鱼邮件的&rdquo;等诸多借口,来推脱自己打来恶意电子邮件的错误行为。</p>
<p>Hayslip补充道</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;无论你采用多少技术手段和工具来阻止它们,它们总有办法成功欺骗用户&rdquo;</td>
</tr>
</tbody>
</table>
<p>近日Webroot公司扫描了过去18个月内的成千上万封网络钓鱼电子邮件以了解有关针对特定目标的常见主题行的发展趋势。Hayslip向全美约100名首席信息安全官展示了此次调查结果并了解到&ldquo;很多人都会收到类似的邮件&rdquo;。在网络钓鱼电子邮件中经常可以看到与财务有关的消息和紧急通知,尽管是在不同的主题之下。</p>
<p>Cofense(前身为PhishMe)的网络安全战略家John&ldquo;Lex&rdquo;Robinson回应了Hayslip的观点并表示攻击者对于他们发送的电子邮件的背景以及他们的攻击目标已经有了越来越深地了解。他说</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;如果把我们今天的沟通方式(包含许多流行语)与15年前、20年前甚至30年前相比可能就显得不那么通俗了。但是网络钓鱼邮件要求的从来都不是通俗而是要与商业行话保持一致。&rdquo;</td>
</tr>
</tbody>
</table>
<p>以下是一些最常见的网络钓鱼主题行,并向大家展示它们所包含的信息,以及它们所揭示的攻击者的目标和策略等内容。</p>
<p><strong>1. 紧急求助</strong></p>
<p style="text-align: center;"><a href="http://s1.51cto.com/oss/201810/29/5057306a0af6920eab7313b53a996a3c.jpg-wh_651x-s_3729779577.jpg" target="_blank"><img src="http://s1.51cto.com/oss/201810/29/5057306a0af6920eab7313b53a996a3c.jpg-wh_651x-s_3729779577.jpg" alt="紧急求助" title="紧急求助" width="auto" height="auto" border="0" /></a></p>
<p>当攻击者不希望目标犹豫不决时,他们就会在主题行中传达一种紧迫感,因为他们希望你能够快速做出决定。</p>
<p>也许该钓鱼邮件不会直接说&ldquo;紧急求助&rdquo;而是选择言语之间存在类似的暗示。Hayslip表示作为一名首席信息安全官他就经常会看到人们因为想要提供帮助而中招而且重要的是很多受害者的心理活动是并不想因为没有采取某些可能很重要的行动而受到惩罚。对于这种情况Hayslip特意告诉员工</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;我宁愿你去寻求专业帮助,也不愿你因为做错事而陷入困境。我建议你可以忽略任何在主题行中言及&lsquo;紧急&rsquo;字眼的未知邮件。因为真的紧急的事情可以通过很多其他更高效的途径来解决,例如打电话。&rdquo;</td>
</tr>
</tbody>
</table>
<p><strong>2. 发票</strong></p>
<p style="text-align: center;"><a href="http://s3.51cto.com/oss/201810/29/06ac27ce87a64de6a45010f77cda9460.jpg" target="_blank"><img src="http://s3.51cto.com/oss/201810/29/06ac27ce87a64de6a45010f77cda9460.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">在Cofense检测到的&ldquo;Top10网络钓鱼邮件主题行&rdquo;中,&ldquo;发票&rdquo;一词就独占6大主题行(只是表达方式有所差异),这也说明在考虑网络钓鱼主题时,财务动机仍然处于主导地位。</p>
<p style="text-align: left;">在谈及Cofense追踪到的最常见的网络钓鱼骗局时Robinson表示</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;对我来说,最有意思的是绝大多数网络钓鱼骗局都涉及金钱主题。很显然,金钱对每个人来说都是一个极具刺激性的话题&hellip;&hellip;当你有了这样一个极具刺激性的话题时,就能立即引起人们的兴趣。&rdquo;</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">虽然前6个骗局的具体消息内容有所不同但所有人都试图用&ldquo;发票&rdquo;一词作为主题来吸引他们的目标。金钱是一个强大的动力攻击者很清楚地知道这一点并且正在利用它来击垮人们的心理防线。根据数据显示在Cofense扫描的网络钓鱼电子邮件中大约有100,000个电子邮件是利用&ldquo;发票&rdquo;一词作为主题的。</p>
<p style="text-align: left;">Cofense研究人员还发现&ldquo;付款汇款&rdquo;是另一个受欢迎的标题其使用量也超过了40,000封电子邮件。同时&ldquo;声明&rdquo;&ldquo;付款&rdquo;也是非常受欢迎的主题。</p>
<p style="text-align: left;">WebRoot则发现&ldquo;电汇&rdquo;也是一种常见的选择,当然还有一些电子邮件会更具体:&ldquo;您最近的Chase付款通知&rdquo;是另一个受欢迎的财务主题。</p>
<p style="text-align: left;">需要注意的是不要以为网络钓鱼欺诈只会发生在没有上网经验的人身上即使是互联网公司也会遭到电汇诈骗而且诈骗数额不小已经达到数百万美元。谷歌和Facebook的会计部门电脑上由于被安装了恶意软件通过查阅它们的转账记录瞄准了一个开发商开了数百万美元的发票。</p>
<p style="text-align: left;">虽然事后经过执法机关的努力,追回了损失;但其他公司就不会这么幸运了2016年价值30亿美元被电汇诈骗绝大部分都无法追回。</p>
<p style="text-align: left;"><strong>3. 银行通知</strong></p>
<p style="text-align: center;"><a href="http://s1.51cto.com/oss/201810/29/9cf0f8daf78f23189e7e68468690abb6.jpg" target="_blank"><img src="http://s1.51cto.com/oss/201810/29/9cf0f8daf78f23189e7e68468690abb6.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">Hayslip表示针对公司高管的经济动机网络钓鱼攻击往往需要攻击者付出更多的努力和研究以及更为严谨的措辞和语法。他解释称</p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" width="95%" cellspacing="0" cellpadding="6" border="0" align="center">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#fdfddf">&ldquo;我认为,这就是&lsquo;捕鲸攻击&rsquo;(whaling attacks)和普通的网络钓鱼攻击之间的区别。所谓&ldquo;捕鲸攻击&rdquo;,实际上是一种欺诈类型,钓鱼者找到某个公司高层或高管团队的姓名和电子邮件地址(此类信息通常会在网页上免费提供),并撰写与这些人员及其公司职位相称的电子邮件。这些电子邮件会试图诱使高管们单击某个链接并访问某个网站,在此恶意软件会下载到其计算机中,并复制按键记录或搜出敏感信息或公司机密。&rdquo;</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">针对高级员工实施网络钓鱼活动的攻击者,会希望其钓鱼邮件信息尽可能真实。他们可能会向行政助理发送包含特定银行名称或&ldquo;紧急协助&rdquo;信号的欺诈性电子邮件。他们会先对目标机构所选择的银行进行研究并尝试模仿银行发布的通知信息。此外攻击者还可能会联系助理称他们的CEO或CFO正在旅行并遇到了一些财务问题需要经济上的帮助并利用诸如此类的借口来证明资产转移的合理性和可信性。</p>
<p style="text-align: left;"><strong>4. 账户验证</strong></p>
<p style="text-align: center;"><a href="http://s4.51cto.com/oss/201810/29/cdde8bad58338f3e1515aa08fa9578ad.jpg" target="_blank"><img src="http://s4.51cto.com/oss/201810/29/cdde8bad58338f3e1515aa08fa9578ad.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">这一主题行与直接的经济收益关系不大,但与知识产权盗窃存在很大关联。这种类型的攻击通常会进行凭证钓鱼(credential phishing),为了在目标网络内获取立足点。许多线上服务都需要有凭证作为确认服务真实性的基础,但是凭证钓鱼就会企图获取线上服务的凭证,因此,凭证一旦遭窃,攻击者就可以直接获取所需的各类信息,包括使用者的账号、密码等等。</p>
<p style="text-align: left;">在进行凭证钓鱼时,就需要通过&ldquo;账户验证&rdquo;之类的请求来引诱你进入登录页面以验证你的凭证。因为在一系列后续活动中,攻击者需要获取到你的用户名和密码等数据,而想要获取这些信息,就可能涉及冒充您经常使用的品牌发送&ldquo;账户验证&rdquo;主题的钓鱼邮件。</p>
<p style="text-align: left;"><strong>5. 拷贝或文档拷贝</strong></p>
<p style="text-align: center;"><a href="http://s3.51cto.com/oss/201810/29/fb75b867b80e55846070b4dd1a0bdf71.jpg" target="_blank"><img src="http://s3.51cto.com/oss/201810/29/fb75b867b80e55846070b4dd1a0bdf71.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">虽然恶意链接在钓鱼电子邮件中日益普遍但Robinson认为附件也仍然很受欢迎且十分有效尤其是与发票、付款通知和声明相关的电子邮件或者是在线订购和结算相关的警报中。</p>
<p style="text-align: left;">这符合攻击者提高他们对业务环境的理解的趋势。因为如果他们知道员工经常发送文件的事实就会知道恶意电子表格或Word文件形式的附件是合理的。越来越多的附件以及将宏视为主要交付方式的事实均表明攻击者越来越善于理解业务环境以至于他们清楚地知道在网络钓鱼电子邮件中放入什么内容才是正常的。</p>
<p style="text-align: left;">此外,许多网络钓鱼邮件的主题都非常短,甚至只有一两个字。这也表明攻击者理解现代商业沟通的方式是相对非正式的。因为商业环境中的人奔波忙碌,不必设置非常正式、具体的主题。</p>
<p style="text-align: left;">就该主题而言,创建标准的沟通流程和政策可以帮助组织有效地防御这种形式的网络钓鱼攻击。组织可以向员工展示这些电子邮件应该来自哪里,以及它们应该采取何种格式,以便他们能够及时发现欺诈性邮件。</p>
<p style="text-align: left;"><strong>6. 行动请求,如&ldquo;支付卖家尾款&rdquo;</strong></p>
<p style="text-align: center;"><a href="http://s1.51cto.com/oss/201810/29/3b6b6bdb4e35d68ccdac7e0ff881fc9e.jpg" target="_blank"><img src="http://s1.51cto.com/oss/201810/29/3b6b6bdb4e35d68ccdac7e0ff881fc9e.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">Hayslip指出目标往往会轻信此类钓鱼邮件主题。在电子邮件中采用&ldquo;我们需要您这样做&rdquo;相关的主题行,往往能够成功地诱使目标完成攻击者需要他们完成的任何事情。调查数据也指出,在面对与&ldquo;行动请求&rdquo;相关的主题时,人们实际上确实是这样做的&mdash;&mdash;在与&ldquo;行动请求&rdquo;相关的钓鱼邮件中恶意链接的点击率高达约40%,攻击者会将受害者重定向到虚假的人力资源网站,以窃取其登录凭证。受害者在被骗后会悔悟称,&ldquo;我知道我当时不应该那样做。&rdquo;</p>
<p style="text-align: left;">在过去一年中,攻击者已经从使用恶意附件转化为嵌入恶意链接。如今,大多数网络钓鱼电子邮件中都包含恶意链接,并且从表面看来,受害者越来越难以判断这些链接是否可以安全点击。因为在过去,他们可以将鼠标悬停在一个链接上来查看它是否可疑;但是随着攻击者的技能升级,这种方式如今已经起不了作用了。</p>
<p style="text-align: left;"><strong>7. 亚马逊/某宝:您的订单#812-4623可能已到达</strong></p>
<p style="text-align: center;"><a href="http://s2.51cto.com/oss/201810/29/561de6b00a7bb4093f4eb699bfc54110.jpg" target="_blank"><img src="http://s2.51cto.com/oss/201810/29/561de6b00a7bb4093f4eb699bfc54110.jpg" alt="" title="" width="auto" height="auto" border="0" /></a></p>
<p style="text-align: left;">Hayslip指出这种类型的网络钓鱼电子邮件通常会在假日期间出现(比如马上就要到来的双11盛典)。他解释称,每逢重大节假日,各类商家都会向目标客户发送大量打折促销、快递动态等邮件,这时候,钓鱼电子邮件也就伺机而动了。此外,一些特定类型的攻击还会在一年中的不同时间段出现:例如,金融和财务相关的骗局会在税收季到来时泛滥;而在圣诞节期间就会出现&ldquo;支付&rdquo;相关的欺诈性消息。</p>
<p style="text-align: left;">Hayslip补充道该消息也可能并不会特别提及包裹是否已经到达它可能会以您最近购买东西的收据为诱饵并附带恶意附件。</p>
<p style="text-align: left;">经常在亚马逊/某宝上购物的人,很大机会会毫不犹豫地点击这些电子邮件,以查看其中的内容确定自己买的什么东西,何时能够到达等等。当然,他们也就会毫不犹豫地点击其中的恶意链接来查看他们多订购的商品,而当他们发现自己的设备已经感染恶意软件时,早已悔之晚矣。</p>
<p style="text-align: left;"><strong>用户应该如何警惕钓鱼邮件?</strong></p>
<ul>
<li>避免开启来路不明的电子邮件及文件,安装杀毒软件并及时升级病毒知识库和操作系统补丁,将敏感信息输入隐私保护,打开个人防火墙;</li>
<li>对要求重新输入账号信息,否则将停掉信用卡账号之类的邮件不予理睬;</li>
<li>尤为重要的是不要回复或者点击邮件的链接,如果你想核实电子邮件的信息,使用电话,而非鼠标;若想访问某个公司的网站,使用浏览器直接访问,而非点击邮件中的链接;</li>
<li>留意网址&ndash;多数合法网站的网址相对较短,通常以.com或者.gov结尾仿冒网站的地址通常较长只是在其中包括合法的企业名字(甚至根本不包含);</li>
<li>不同账号使用不同口令,不要使用同样的口令;</li>
<li>不要使用很简单的口令,(如000000、生日等);</li>
</ul>
<p style="text-align: left;">最后提醒一句,不幸中招者最好尽快更换相关密码和取消信用卡。</p>
<p>【编辑推荐】</p>
<div>
<ol>
<li><a href="http://netsecurity.51cto.com/art/201810/585733.htm" target="_blank">AI攻击怕不怕 多数安全专家表示怕怕</a></li>
<li><a href="http://zhuanlan.51cto.com/art/201810/585770.htm" target="_blank">企业风险管理(ERM):如何将网络安全威胁融入业务上下文</a></li>
<li><a href="http://netsecurity.51cto.com/art/201810/585796.htm" target="_blank">聚焦金融网络犯罪团伙Cobalt Gang 的Commodity Builder 及其infrastructure攻击</a></li>
<li><a href="http://netsecurity.51cto.com/art/201810/585809.htm" target="_blank">黑客将Python作为攻击编码语言的首选</a></li>
<li><a href="http://netsecurity.51cto.com/art/201810/585824.htm" target="_blank">网络攻击者可以使用你的特权用户凭证的3种隐藏方式</a></li>
</ol>
</div>
<div align="right">【责任编辑:<a class="ln" href="mailto:sunsj@51cto.com">赵宁宁</a> TEL01068476606】</div><br>
<a href="###" class="dzdz zhan abc" target="_self">点赞 <span>0</span></a>
</div>
<div class="share5">
<ul>
<li><a href='http://www.51cto.com/php/search.php?keyword=%CD%F8%C2%E7%B5%F6%D3%E3' target='_blank' class='underline'>网络钓鱼</a>&nbsp;&nbsp;<a href='http://www.51cto.com/php/search.php?keyword=%B9%A5%BB%F7' target='_blank' class='underline'>攻击</a>&nbsp;&nbsp;<a href='http://www.51cto.com/php/search.php?keyword=%CD%F8%C2%E7%B0%B2%C8%AB' target='_blank' class='underline'>网络安全</a></li>
</ul>
<dl>
<dt><em>分享:</em>
<div class="bdsharebuttonbox" data-tag="share_1">
<a class="wb" data-cmd="tsina"></a>
<a class="wx" data-cmd="weixin"></a>
<a class="more" data-cmd="more"></a>
</div>
</dt>
<script type="text/javascript">
window._bd_share_config = {
common : {
bdText : document.title
},
share : [{
"bdSize" : 16,
}]
}
with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];
</script>
<!-- Baidu Button END -->
</dl>
</div>
<div class="nrdp comment">
<script charset="utf-8" id="ParadigmSDKv3" src="https://nbrecsys.4paradigm.com/sdk/js/ParadigmSDK_v3.js"></script>
<!--<div id='c21148bdd1e7483e87966a00982902d4'></div>-->
<script>
var host = window.location.host;
var domain_prefix = host.replace(/\.51cto\.com/, '');
function appendRecommend(){
$(".share5").prepend("<div id='c21148bdd1e7483e87966a00982902d4'></div>");
}
function clock(){
$(".dzk").hide();
var isShow = $(".dzk").css("display");
if(isShow == 'none'){
clearInterval(timer);
}
}
if(domain_prefix == 'developer') {
appendRecommend();
ParadigmSDKv3.init("dcde33d0dc6845609e9cb47f754d1094");
ParadigmSDKv3.renderArticle('c21148bdd1e7483e87966a00982902d4',346,605);
var timer = setInterval("clock()",50);
}
</script>
<div class='comment center'>
<div class='inner center' id="cmscmt_iframe"></div>
</div>
<script type="text/javascript" id="UYScript" src="http://comment.51cto.com/static/js/api_js/iframe_cmt.js" async=""></script>
</div>
<div class="dzk">
<dl><dt class="show">大家都在看</dt><dt>猜你喜欢</dt></dl>
<div>
<ul>
<li class="show">
<div class="djdzk" id="djdzk">
</div>
</li>
<li>
<div class="djdzk" id="cnxh"></div>
</li>
</ul>
</div>
</div>
</div>
<!-- 文章左侧结束 -->
<!-- 文章右侧 -->
<div class="wrap_right">
<div><!-- intel广告位 -->
<span class="mtgg" id="right1_ad" style="height:auto; width:300px;margin-bottom: 10px;">
<script type="text/javascript">
var s=window.location.toString();
var s1=s.substr(7,s.length);
var s2=s1.indexOf(".");
s=s.substr(7,s2);
if ( s == 'stor') {
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg1.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=987");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
}else if (s == 'server'){
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg1.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=1061");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
}else if(s == 'network'){
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg1.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=985");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
// var str = '<div class="m30" style="position:relative;">';
// str += '<iframe src="http://network.51cto.com/act/cisco/art201709" scrolling="no" frameborder="0" sytle="" width="300" align="middle" height="360" target ="_parent"></iframe>';
// str +='<div style="left: 0px; width: 24px; height: 14px; z-index: 12; position: absolute; background: transparent url(http://s5.51cto.com/wyfs02/M00/86/BB/wKiom1fI4nWStYqXAAAEoZQn6vs942.png)repeat scroll 0% 0%; bottom: 2px;"></div>';
// str += '</div>';
// $('#right1_ad').html(str);
}
// var timestamp = Date.parse(new Date());
//timestamp = timestamp / 1000;
if (s == 'cloud') {
// var str = "<ins class='dcmads' style='display:inline-block;width:300px;height:360px' data-dcm-placement='N5751.51CTO/B11527656.153459357' data-dcm-rendering-mode='script' data-dcm-http-only data-dcm-app-id=''></ins>";
//
// $('#right1_ad').html(str);
//1496592000
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg3.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=969");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
}
if (s == 'bigdata') {
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg2.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=970");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
}
//var timestamp = Date.parse(new Date());
//timestamp = timestamp / 1000;
// if(s == 'netsecurity'&& (timestamp >= 1506182400)){
// var str = '<div class="m30" style="position:relative;">';
// str += '<iframe src="http://netsecurity.51cto.com/act/cisco/art201709" scrolling="no" frameborder="0" sytle="" width="300" align="middle" height="360" target ="_parent"></iframe>';
// str +='<div style="left: 0px; width: 24px; height: 14px; z-index: 12; position: absolute; background: transparent url(http://s5.51cto.com/wyfs02/M00/86/BB/wKiom1fI4nWStYqXAAAEoZQn6vs942.png)repeat scroll 0% 0%; bottom: 2px;"></div>';
// str += '</div>';
// $('#right1_ad').html(str);
// }
</script>
<script src='http://www.googletagservices.com/dcm/dcmads.js'></script>
</span></div>
<div class="mtgg m30"><script type="text/javascript" src="http://image.51cto.com/ad/art/hzh/ad.js"></script></div>
<div><!--推荐系统start-->
<script charset="utf-8" id="ParadigmSDKv3" src="https://nbrecsys.4paradigm.com/sdk/js/ParadigmSDK_v3.js"></script>
<style>
.article-box{margin-bottom: 20px;}
</style>
<div id='279ee0c8d3f94395a91a27085384c3b9'></div>
<script>
var host = window.location.host;
var domain_prefix = host.replace(/\.51cto\.com/, '');
ParadigmSDKv3.init("dcde33d0dc6845609e9cb47f754d1094");
if(domain_prefix == 'developer'){
ParadigmSDKv3.renderArticle('279ee0c8d3f94395a91a27085384c3b9',346,682);
}else{
ParadigmSDKv3.renderArticle('279ee0c8d3f94395a91a27085384c3b9',269,460);
}
var timer = setInterval("clock()",50);
var clock = function(){
$('.paradigm-recomm-logo').css({'width':'100px'});
var w = $('.paradigm-recomm-logo').width();
if(w == 100){
clearInterval(timer);
}
}
</script>
<!--推荐系统end-->
</div>
<div class="bjtj m30">
<h2><span>编辑推荐</span></h2>
<dl><dt>原创</dt><dd><a href="http://netsecurity.51cto.com/art/201810/585722.htm" title="雅虎黑客案和解,可企业数据泄露事件并未停止">雅虎黑客案和解,可企业数据泄露事件并未停止</a></dd></dl><dl><dt>原创</dt><dd><a href="http://netsecurity.51cto.com/art/201810/585257.htm" title="走进几维安全听CTO刘柏江讲述他们想做的事儿">走进几维安全听CTO刘柏江讲述他们想做的事儿</a></dd></dl><dl><dt>热点</dt><dd><a href="http://netsecurity.51cto.com/art/201810/585371.htm" title="浅谈公共云安全三大认知误区">浅谈公共云安全三大认知误区</a></dd></dl><dl><dt>聚焦</dt><dd><a href="http://netsecurity.51cto.com/art/201810/585354.htm" title="黑客基础Metasploit模块简介渗透攻击模块、攻击载荷模块">黑客基础Metasploit模块简介渗透攻击模块、攻击载荷模块</a></dd></dl><dl><dt>头条</dt><dd><a href="http://netsecurity.51cto.com/art/201810/585235.htm" title="企业被“勒索”遭殃,企业数据安全路在何方">企业被“勒索”遭殃,企业数据安全路在何方</a></dd></dl>
</div>
<div></div>
<div class="news m30">
<dl><dt class="show">24H热文</dt><dt>一周话题</dt><dt>本月最赞</dt></dl>
<ul><li class="show"><a href=http://netsecurity.51cto.com/art/201808/581172.htm title=2018年十大局域网监控工具网络管理员值得一试>2018年十大局域网监控工具网络管理员值得一试</a><a href=http://netsecurity.51cto.com/art/200909/149683.htm title=上网行为管理的发展和现状>上网行为管理的发展和现状</a><a href=http://netsecurity.51cto.com/art/201004/193632.htm title=浅析上网行为管理存在价值和理解误区>浅析上网行为管理存在价值和理解误区</a><a href=http://netsecurity.51cto.com/art/201702/531606.htm title=安卓系统里最好用的VPN工具汇总>安卓系统里最好用的VPN工具汇总</a><a href=http://netsecurity.51cto.com/art/201107/276208.htm title=上网行为管理路由器你选对了吗?>上网行为管理路由器你选对了吗?</a><a href=http://netsecurity.51cto.com/art/201112/307746.htm title=数据防泄密DLP产品类型浅析>数据防泄密DLP产品类型浅析</a><a href=http://netsecurity.51cto.com/art/201703/534641.htm title=最值得推荐的5个VPN服务>最值得推荐的5个VPN服务</a><a href=http://netsecurity.51cto.com/art/200709/57185.htm title=ARP欺骗攻击原理也可以这样理解>ARP欺骗攻击原理也可以这样理解</a></li><li><a href=http://netsecurity.51cto.com/art/201808/581172.htm title=2018年十大局域网监控工具网络管理员值得一试>2018年十大局域网监控工具网络管理员值得一试</a><a href=http://netsecurity.51cto.com/art/200909/149683.htm title=上网行为管理的发展和现状>上网行为管理的发展和现状</a><a href=http://netsecurity.51cto.com/art/201004/193632.htm title=浅析上网行为管理存在价值和理解误区>浅析上网行为管理存在价值和理解误区</a><a href=http://netsecurity.51cto.com/art/201107/276208.htm title=上网行为管理路由器你选对了吗?>上网行为管理路由器你选对了吗?</a><a href=http://netsecurity.51cto.com/art/201702/531606.htm title=安卓系统里最好用的VPN工具汇总>安卓系统里最好用的VPN工具汇总</a><a href=http://netsecurity.51cto.com/art/201703/534641.htm title=最值得推荐的5个VPN服务>最值得推荐的5个VPN服务</a><a href=http://netsecurity.51cto.com/art/201112/307746.htm title=数据防泄密DLP产品类型浅析>数据防泄密DLP产品类型浅析</a><a href=http://netsecurity.51cto.com/art/201802/565745.htm title=最值得推荐的五个付费VPN服务>最值得推荐的五个付费VPN服务</a></li><li><a href=http://netsecurity.51cto.com/art/201808/581172.htm title=2018年十大局域网监控工具网络管理员值得一试>2018年十大局域网监控工具网络管理员值得一试</a><a href=http://netsecurity.51cto.com/art/200909/149683.htm title=上网行为管理的发展和现状>上网行为管理的发展和现状</a><a href=http://netsecurity.51cto.com/art/201004/193632.htm title=浅析上网行为管理存在价值和理解误区>浅析上网行为管理存在价值和理解误区</a><a href=http://netsecurity.51cto.com/art/201702/531606.htm title=安卓系统里最好用的VPN工具汇总>安卓系统里最好用的VPN工具汇总</a><a href=http://netsecurity.51cto.com/art/201703/534641.htm title=最值得推荐的5个VPN服务>最值得推荐的5个VPN服务</a><a href=http://netsecurity.51cto.com/art/201107/276208.htm title=上网行为管理路由器你选对了吗?>上网行为管理路由器你选对了吗?</a><a href=http://netsecurity.51cto.com/art/201802/565745.htm title=最值得推荐的五个付费VPN服务>最值得推荐的五个付费VPN服务</a><a href=http://netsecurity.51cto.com/art/201112/307746.htm title=数据防泄密DLP产品类型浅析>数据防泄密DLP产品类型浅析</a></li></ul>
</div>
<div><div class="areaAd mt5" id="ad_1"></div>
<div style="display:none">
<span id="ad1">
<script type='text/javascript'><!--//<![CDATA[
var m3_u = (location.protocol=='https:'?'https://gg.51cto.com/www/delivery/ajs.php':'http://gg3.51cto.com/www/delivery/ajs.php');
var m3_r = Math.floor(Math.random()*99999999999);
if (!document.MAX_used) document.MAX_used = ',';
document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
document.write ("?zoneid=693");
document.write ('&amp;cb=' + m3_r);
if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
document.write ("&amp;loc=" + escape(window.location));
if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
if (document.context) document.write ("&context=" + escape(document.context));
if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
document.write ("'><\/scr"+"ipt>");
//]]>--></script><noscript><a href='//gg2.51cto.com/www/delivery/ck.php?n=ae95ac07&amp;cb=INSERT_RANDOM_NUMBER_HERE' target='_blank'><img src='//gg.51cto.com/www/delivery/avw.php?zoneid=693&amp;cb=INSERT_RANDOM_NUMBER_HERE&amp;n=ae95ac07' border='0' alt='' /></a></noscript>
</span>
</div>
<script>
document.getElementById('ad_1').innerHTML = document.getElementById('ad1').innerHTML;
</script></div>
<div class="spkc m30">
<h2><span>视频课程</span><a href="http://edu.51cto.com/">+更多</a></h2>
<dl>
<dt><a href="http://edu.51cto.com/course/course_id-13963.html"><img src="https://s1.51cto.com/images/201806/16/cb25f91dd59d07ad1a62030e24c802e4.png?x-oss-process=image/resize,m_fixed,h_94,w_124" title="诸神之眼 - Nmap扫描工具 基础-主机发现视频教程全套Nmap专题更优惠" alt="诸神之眼 - Nmap扫描工具 基础-主机发现视频教程全套Nmap专题更优惠" width="100px" height="80px"></a><span></span></dt>
<dd>
<h3><a href="http://edu.51cto.com/course/course_id-13963.html" target="_blank" title="诸神之眼 - Nmap扫描工具 基础-主机发现视频教程全套Nmap专题更优惠">诸神之眼 - Nmap扫描工具 基础-主机发现视频</a></h3>
<h4><span class="fl">讲师:<em><a href="http://edu.51cto.com/lecturer/user_id-12102806.html" target="_blank">刘晓阳</a></em></span><span class="fr"><em>949</em>人学习过</span></h4>
</dd>
</dl>
<dl>
<dt><a href="http://edu.51cto.com/course/course_id-12185.html"><img src="https://s1.51cto.com/images/201706/22/667e5ec121dd0c02e7baf0c933b7b52d.png?x-oss-process=image/resize,m_fixed,h_94,w_124" title="网络安全之最新华为USG防火墙的理论及实战视频课程" alt="网络安全之最新华为USG防火墙的理论及实战视频课程" width="100px" height="80px"></a><span></span></dt>
<dd>
<h3><a href="http://edu.51cto.com/course/course_id-12185.html" target="_blank" title="网络安全之最新华为USG防火墙的理论及实战视频课程">网络安全之最新华为USG防火墙的理论及实战视</a></h3>
<h4><span class="fl">讲师:<em><a href="http://edu.51cto.com/lecturer/user_id-9130833.html" target="_blank">李熠芳</a></em></span><span class="fr"><em>3047</em>人学习过</span></h4>
</dd>
</dl>
<dl>
<dt><a href="http://edu.51cto.com/course/course_id-14219.html"><img src="https://s1.51cto.com/images/201807/09/7959a2e0fba12d5e3043e9507275b271.png?x-oss-process=image/resize,m_fixed,h_94,w_124" title="诸神之眼 - Nmap扫描工具 大型网络扫描视频教程" alt="诸神之眼 - Nmap扫描工具 大型网络扫描视频教程" width="100px" height="80px"></a><span></span></dt>
<dd>
<h3><a href="http://edu.51cto.com/course/course_id-14219.html" target="_blank" title="诸神之眼 - Nmap扫描工具 大型网络扫描视频教程">诸神之眼 - Nmap扫描工具 大型网络扫描视频教</a></h3>
<h4><span class="fl">讲师:<em><a href="http://edu.51cto.com/lecturer/user_id-12102806.html" target="_blank">刘晓阳</a></em></span><span class="fr"><em>208</em>人学习过</span></h4>
</dd>
</dl>
</div>
<div id="zxf0309" class="fl m30">
<div class="Central_bank mb20">
<div class="titall">
<a href="http://club.51cto.com/act/cto/caff?51CTOQ" class="zlmore" target="_blank">+ 更多</a>
<div class="mintitall">
<span class="cur">CTO品牌</span>
</div>
</div>
<div class="jp-list" >
<div class="rmzw_xx">
<ul>
<li><span class="ico"></span><a href="http://siliconvalley2018.mikecrm.com/qMTa1RQ" target="_blank">CTO训练营第六季抢先报名申请表</a></li>
<li><span class="ico"></span><a href="http://x.51cto.com/act/cto/camp/page/course_list/cid/98" target="_blank">金融科技时代CTO的破局之道</a></li>
<li><span class="ico"></span><A href="http://siliconvalley2018.mikecrm.com/RGszRhY" target="_blank">线上社群,仅限技术管理者免费申请</A></li>
</ul>
</div>
<dl class="ctoll clearfix">
<dt><a href="http://x.51cto.com?51ctoQ" target="_blank"><span style="color:#BE0000">CTO训练营</span></a></dt>
<dd>
<A href="http://x.51cto.com/act/cto/camp/page/enroll" target="_blank">申请入营&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</A>
<A href="http://x.51cto.com/act/cto/camp/page/course_list/cid/83?51ctoQ" target="_blank">互联网班&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</A>
<A href="http://x.51cto.com/act/cto/camp/page/course_list/cid/98?51ctoQ" target="_blank">金融班</A>
</dd>
</dl>
<dl class="ctoll clearfix">
<dt><a href="http://club.51cto.com?51ctoQ" target="_blank"><span style="color:#BE0000">CTO俱乐部</span></a></dt>
<dd>
<A href="http://club.51cto.com/act/cto/caff/page/member-notice?51ctoQ" target="_blank">申请加入&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</A>
<A href="http://club.51cto.com/act/cto/caff/page/activity-list?51ctoQ" target="_blank">最新活动&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</A>
<A href="http://club.51cto.com/act/cto/caff/page/year-list?51ctoQ" target="_blank">全部课程</A>
</dd>
</dl>
</div>
</div>
</div>
<div></div>
<div></div>
<div class="zxzt m30">
<h2><span>最新专题</span><a href="http://netsecurity.51cto.com/speclist/1074">+更多</a></h2>
<dl>
<dt><a href="http://netsecurity.51cto.com/art/201810/585738.htm" title="关注网络安全 预防网络诈骗"><img src="https://s3.51cto.com/oss/201810/26/9d077d891ece918a8ed9985d2d55320d.jpg-wh_100x70-s_2209487519.jpg" alt="关注网络安全 预防网络诈骗" title="关注网络安全 预防网络诈骗"></a></dt>
<dd>
<a href="http://netsecurity.51cto.com/art/201810/585738.htm" title="关注网络安全 预防网络诈骗">关注网络安全 预防网络诈骗</a>
<h3><a href='http://www.51cto.com/php/search.php?keyword=%CD%F8%C2%E7%D5%A9%C6%AD'>网络诈骗</a></h3>
</dd>
</dl><dl>
<dt><a href="http://netsecurity.51cto.com/art/201808/581030.htm" title="2018美国黑帽大会专题报道"><img src="https://s4.51cto.com/oss/201808/10/347f7f094ab76f6edf0003b9d3744c94.jpg-wh_100x70-s_3573024760.jpg" alt="2018美国黑帽大会专题报道" title="2018美国黑帽大会专题报道"></a></dt>
<dd>
<a href="http://netsecurity.51cto.com/art/201808/581030.htm" title="2018美国黑帽大会专题报道">2018美国黑帽大会专题报道</a>
<h3><a href='http://www.51cto.com/php/search.php?keyword=%BA%DA%C3%B1%B4%F3%BB%E1'>黑帽大会</a></h3>
</dd>
</dl><dl>
<dt><a href="http://netsecurity.51cto.com/art/201804/571743.htm" title="2018第五届首都网络安全日专题报道"><img src="https://s3.51cto.com/oss/201804/27/9bc39ff8302de958e56ac7acfb254a19.jpg-wh_100x70-s_3558624953.jpg" alt="2018第五届首都网络安全日专题报道" title="2018第五届首都网络安全日专题报道"></a></dt>
<dd>
<a href="http://netsecurity.51cto.com/art/201804/571743.htm" title="2018第五届首都网络安全日专题报道">2018第五届首都网络安全日专题报道</a>
<h3><a href='http://www.51cto.com/php/search.php?keyword=%CA%D7%B6%BC%CD%F8%C2%E7%B0%B2%C8%AB%C8%D5'>首都网络安全日</a></h3>
</dd>
</dl><dl>
<dt><a href="http://netsecurity.51cto.com/art/201804/570631.htm" title="2018美国RSA信息安全大会专题报道"><img src="https://s2.51cto.com/oss/201804/16/744f4ecc960102e6d9dc4fe351d31ea9.png-wh_100x70-s_246488505.png" alt="2018美国RSA信息安全大会专题报道" title="2018美国RSA信息安全大会专题报道"></a></dt>
<dd>
<a href="http://netsecurity.51cto.com/art/201804/570631.htm" title="2018美国RSA信息安全大会专题报道">2018美国RSA信息安全大会专题报道</a>
<h3><a href='http://www.51cto.com/php/search.php?keyword=RSA'>RSA</a></h3>
</dd>
</dl>
</div>
<div></div>
<div id="jcpl"></div>
<div></div>
<div class="news m30">
<dl><dt class="show">精选博文</dt><dt>论坛热帖</dt><dt>下载排行</dt></dl>
<div>
<ul>
<li class="show">
<a href="http://sery.blog.51cto.com/10037/2309999/" target="_blank" title="proxmox超融合集群用户授权">proxmox超融合集群用户授权</a><a href="http://13706760.blog.51cto.com/13696760/2309915/" target="_blank" title="在LNMP架构中搭建zabbix监控服务">在LNMP架构中搭建zabbix监控服务</a><a href="http://13981273.blog.51cto.com/13971273/2309905/" target="_blank" title="30分钟入门比特币与区块链">30分钟入门比特币与区块链</a><a href="http://13746824.blog.51cto.com/13736824/2309868/" target="_blank" title="自动化运维-Ansible 第三部Playbook 介绍)">自动化运维-Ansible 第三部Playb</a><a href="http://xjsunjie.blog.51cto.com/999372/2309332/" target="_blank" title="数据中心:一个支点,撬动全世界">数据中心:一个支点,撬动全世界</a>
</li>
<li>
<a href="http://bbs.51cto.com/thread-1506478-1.html" target="_blank" title="【其他】看看你能拿到PHP的什么学位">【其他】看看你能拿到PHP的什么学位</a><a href="http://bbs.51cto.com/thread-1515497-1.html" target="_blank" title="【小白必备】Python入门学习必备的资料未完待续...">【小白必备】Python入门学习必备的资</a><a href="http://bbs.51cto.com/thread-1515732-1.html" target="_blank" title="【教程经验】悬赏:函数调用的性能研究讨论">【教程经验】悬赏:函数调用的性能研</a><a href="http://bbs.51cto.com/thread-1515871-1.html" target="_blank" title="Excel 十大学习体系-表哥、表姐、表神之路必会含47门实战课程推荐">Excel 十大学习体系-表哥、表姐、表</a><a href="http://bbs.51cto.com/thread-1515895-1.html" target="_blank" title="IT运维10年的茫然">IT运维10年的茫然</a>
</li>
<li>
<a href="http://down.51cto.com/data/2220121/" target="_blank" title="javaservlet学习笔记">javaservlet学习笔记</a><a href="http://down.51cto.com/data/2220120/" target="_blank" title="confluence5.1-crack">confluence5.1-crack</a><a href="http://down.51cto.com/data/2220119/" target="_blank" title="20150729-电流电压互感器检验标准">20150729-电流电压互感器检验标准</a><a href="http://down.51cto.com/data/2220118/" target="_blank" title="JSP+ext+人力资源管理系统">JSP+ext+人力资源管理系统</a><a href="http://down.51cto.com/data/2220117/" target="_blank" title="JNI资料大全">JNI资料大全</a>
</li>
</ul>
</div>
</div>
<div></div>
<div class="ds m30">
<h2><span>读 书 </span><a href="http://book.51cto.com/">+更多</a></h2>
<dl>
<dt><a href="http://book.51cto.com/art/200707/51132.htm" title="SOA概念、技术与设计"><img src="http://images.51cto.com/files/uploadimg/20070712/160915523.gif" width="98px" height="144px"/></a></dt>
<dd><h3><a href="http://book.51cto.com/art/200707/51132.htm" title="SOA概念、技术与设计">SOA概念、技术与设计</a></h3>
在本书中Thomas ERL呈现了第一部端对端的教程提供了从基层开始的面向服务的建模与设计的逐步指导。通过逐步的、清晰生动的、良好的SOA...
</dd>
</dl>
</div>
<div></div>
<div class="dydy">
<dl>
<dt><img src="https://static1.51cto.com/51cto/cms/2016/images/dydy.jpg" alt=""></dt>
<dd>
<h3>订阅51CTO邮刊</h3>
<h4><a href="http://news.51cto.com/col/1323/">点击这里查看样刊</a></h4>
<a href="http://home.51cto.com/index.php?s=/Subscribe"><img src="https://static4.51cto.com/51cto/cms/2016/images/ljdy.jpg" alt="订阅51CTO邮刊"></a>
</dd>
</dl>
</div>
<div></div>
</div>
<!-- 文章右侧结束 -->
</div>
<div class="footer_nav">
<div class="wrap">
<h2>51CTO旗下网站</h2>
<a href="http://www.51cto.com">领先的IT技术网站 51CTO</a>|<a href="http://cio.51cto.com">中国专业CIO网站 CIOage </a>|
<a href="http://www.hc3i.cn">中国数字医疗领军网站 HC3i</a>
<!--|<a href="http://zhijiapro.com/">区块链聚合媒体 zhijiapro</a>-->
</div>
</div>
<div id="ft"><div id="foot" align="center"><script src="http://www.51cto.com/images/copy_right/copy_right.js?v=0.5"></script></div></div>
<div class="clk"><a class="ewm" href="###" target="_self"><img src="https://s5.51cto.com/wyfs02/M01/9B/36/wKiom1lfV4XgxZ3zAADAK8-_E6k046.jpg" style="display: none;"></a><a class="yjk" href="#comment" target="_self"></a><a class="topx" href="#topx" target="_self"></a></div>
<script>
$(function(){
var host = window.location.host;
document.getElementById('cnxh').style.display = '';
});
var $$ = function(func){
if (document.addEventListener) {
window.addEventListener("load", func, false);
}
else if (document.attachEvent) {
window.attachEvent("onload", func);
}
};
$$(function(){
show();
});
var show = function(){
var aa = $('#cmscmt_iframe dl').html();
setTimeout(function(){
$("[data-track]").live("click", function() {
if (aa.indexOf('uid') > -1) {
var label = $(this).data("track") + '-login';
} else {
var label = $(this).data("track") + '-not-login';
}console.log(label);
window._hmt && window._hmt.push(['_trackEvent', label, 'click']);
});
}, 3000);
}
</script>
<script src="http://logs.51cto.com/rizhi/count/count.js"></script>
<!-- 学院广告位 -->
<!--<div id="edu_adver">-->
<!--<div class="aderbox">-->
<!--<span class="educlose">×</span>-->
<!--<a href="http://edu.51cto.com/activity/6.html?qd=CMSzuoce"><img src="https://s5.51cto.com/oss/201712/05/94601626f864124ca789425588a5a1f5.jpg" width="100" height="300" alt="51CTO学院双十二活动" title="51CTO学院双十二活动"></a>-->
<!--</div>-->
<!--</div>-->
<style type="text/css">
#edu_adver{position:fixed; top:240px; left:50%; margin-left:-623px; width:100px; height:300px; border:1px solid #c4c4c6;}
#edu_adver .aderbox{position:relative;}
#edu_adver .educlose{position:absolute; right:6px; top:6px; background:#655c4d; color:#fff; font-size:12px; display:inline-block; width:13px; height:13px; text-align:center; line-height:13px; cursor:pointer;}
</style>
<script>
$('#edu_adver .educlose').click(function () {
$("#edu_adver").hide();
});
var s=window.location.toString();
var s1=s.substr(7,s.length);
var s2=s1.indexOf(".");
s=s.substr(7,s2);
if(s=="stor"||s=="server"||s=="network"||s=="virtual"){
$("#edu_adver").hide();
}
if (s=="stor"||s=="server"||s=="network"||s=="virtual") {
$('.aderbox').find('a')[0].href = '';
$('.aderbox').find('img')[0].src = 'https://s2.51cto.com/oss/201711/01/507eecf589ca025c76a3aff2dfc1fcc4.jpg';
}
</script>
<!-- <script src="http://home.51cto.com/iframe/iframe-member-adv?www"></script> -->
<!--数据统计-->
<script>
//ParadigmSDKv3.init("dcde33d0dc6845609e9cb47f754d1094");
if(domain_prefix == 'developer') {
ParadigmSDKv3.trackDetailPageShow(346);
}else{
ParadigmSDKv3.trackDetailPageShow(269);
}
</script>
<script>
var host = window.location.host;
var domain_prefix = host.replace(/\.51cto\.com/, '');
var nowTime = new Date().getTime();
//get start time and end
var startTime1 = 0;
var endTime1 = 0;
var startTime2 = 0;
var endTime2 = 0;
var startTime3 = 0;
var endTime3 = 0;
var startTime4 = 0;
var endTime4 = 0;
var startTime5 = 0;
var endTime5 = 0;
var adContent = '';
if(domain_prefix == 'cloud'){
startTime1 = new Date('2018/08/31 00:00:00').getTime();
endTime1 = new Date('2018/08/31 23:59:59').getTime();
startTime2 = new Date('2018/09/05 00:00:00').getTime();
endTime2 = new Date('2018/09/07 23:59:59').getTime();
startTime3 = new Date('2018/09/12 00:00:00').getTime();
endTime3 = new Date('2018/09/13 23:59:59').getTime();
startTime4 = new Date('2018/09/19 00:00:00').getTime();
endTime4 = new Date('2018/09/20 23:59:59').getTime();
startTime5 = new Date('2018/09/25 00:00:00').getTime();
endTime5 = new Date('2018/09/26 23:59:59').getTime();
if((nowTime > startTime1 && nowTime < endTime1) || (nowTime > startTime2 && nowTime < endTime2) || (nowTime > startTime3 && nowTime < endTime3) || (nowTime > startTime4 && nowTime < endTime4) || (nowTime > startTime5 && nowTime < endTime5)){
adContent = "<iframe src=\"http://cloud.51cto.com/act/IBM/201808ad\" frameborder=\"0\" width=\"300px\" height=\"250px\" scrolling=\"no\"></iframe>";
changeAd();
}
}else if(domain_prefix == 'bigdata'){
startTime1 = new Date('2018/08/31 00:00:00').getTime();
endTime1 = new Date('2018/08/31 23:59:59').getTime();
startTime2 = new Date('2018/09/04 00:00:00').getTime();
endTime2 = new Date('2018/09/04 23:59:59').getTime();
startTime3 = new Date('2018/09/10 00:00:00').getTime();
endTime3 = new Date('2018/09/10 23:59:59').getTime();
startTime4 = new Date('2018/09/17 00:00:00').getTime();
endTime4 = new Date('2018/09/17 23:59:59').getTime();
startTime5 = new Date('2018/09/27 00:00:00').getTime();
endTime5 = new Date('2018/09/27 23:59:59').getTime();
if((nowTime > startTime1 && nowTime < endTime1) || (nowTime > startTime2 && nowTime < endTime2) || (nowTime > startTime3 && nowTime < endTime3) || (nowTime > startTime4 && nowTime < endTime4) || (nowTime > startTime5 && nowTime < endTime5)){
adContent = "<iframe src=\"http://cloud.51cto.com/act/IBM/201808ad2\" frameborder=\"0\" width=\"300px\" height=\"250px\" scrolling=\"no\"></iframe>";
changeAd();
}
}
function changeAd(){
//add new
$("div.mtgg").after(adContent);
//move some block to left
var adBox = $("div.mtgg");
$(".zwnr p:first").before(adBox);
$("div.mtgg").css('margin-top', '7px');
$("div.mtgg").css('margin-right', '10px');
}
</script>
<script type="text/javascript">var artid = 585852</script>
<script src='http://home.51cto.com/index.php?s=/Index/getLoginStatus2015/reback/http%253A%252F%252Fnetsecurity.51cto.com%252Fart%252F201810%252F585852.htm' charset="utf-8"></script>
<script type="text/javascript" src="https://static4.51cto.com/51cto/cms/2016/js/article.js?v=1.0"></script>
<script type="text/javascript" src="https://static5.51cto.com/51cto/cms/2016/js/article_ajax.js?v=2.1"></script>
<script src="http://other.51cto.com/php/count.php?view=yes&artID=585852" type="text/javascript"></script>
<script type="text/javascript" src="http://home.51cto.com/apps/favorite/Tpl/default/Public/js/favorbox.js"></script>
<!-- 一大波JS来袭 -->
<div id="MyMoveAd" style="display:none">
<span id="pinglun"><script type="text/javascript" src="http://other.51cto.com/php/getArtCount.php?artid=585852&type=all"></script></span>
<span id="tonglan"><script type="text/javascript" src="http://image.51cto.com/ad/art/tonglan/ad.js"></script></span>
<span id="wordlink_1"><script src="http://image.51cto.com/ad/art/wordlink/wordlink1.js"></script></span>
<span id="wordlink_2"><script src="http://image.51cto.com/ad/art/wordlink/wordlink2.js"></script></span>
<span id="wordlink_3"><script src="http://image.51cto.com/ad/art/wordlink/wordlink3.js"></script></span>
<span id="wordlink_4"><script src="http://image.51cto.com/ad/art/wordlink/wordlink4.js"></script></span>
<span id="wordlink"><script src="http://image.51cto.com/ad/art/wordlink/ad.js"></script></span>
</div>
<script type="text/javascript">
var thistid=585852;
//收藏按钮
var favor_url = 'http://netsecurity.51cto.com/art/201810/585852.htm';
var favor_title = '警惕7大最常见的网络钓鱼主题行看看你中招没';
document.getElementById('tonglanad').innerHTML=document.getElementById('tonglan').innerHTML;
</script>
<!-- 结束 -->
</body>
</html>

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="iso-8859-15"> <title>meta charset attribute</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="The character encoding of the page can be set by a meta element with charset attribute.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
</head>
<body>
<p class='title'>meta charset attribute</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with charset attribute.</p>
<div class="notes"><p><p>The only character encoding declaration for this HTML file is in the charset attribute of the meta element, which declares the encoding to be ISO 8859-15.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-015">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-009<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-009" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>meta content attribute</title>
<link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
<link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'>
<link rel="stylesheet" type="text/css" href="./generatedtests.css">
<script src="http://w3c-test.org/resources/testharness.js"></script>
<script src="http://w3c-test.org/resources/testharnessreport.js"></script>
<meta name='flags' content='http'>
<meta name="assert" content="The character encoding of the page can be set by a meta element with http-equiv and content attributes.">
<style type='text/css'>
.test div { width: 50px; }</style>
<link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css">
</head>
<body>
<p class='title'>meta content attribute</p>
<div id='log'></div>
<div class='test'><div id='box' class='ýäè'>&#xA0;</div></div>
<div class='description'>
<p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with http-equiv and content attributes.</p>
<div class="notes"><p><p>The only character encoding declaration for this HTML file is in the content attribute of the meta element, which declares the encoding to be ISO 8859-15.</p><p>The test contains a div with a class name that contains the following sequence of bytes: 0xC3 0xBD 0xC3 0xA4 0xC3 0xA8. These represent different sequences of characters in ISO 8859-15, ISO 8859-1 and UTF-8. The external, UTF-8-encoded stylesheet contains a selector <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. This matches the sequence of bytes above when they are interpreted as ISO 8859-15. If the class name matches the selector then the test will pass.</p></p>
</div>
</div>
<div class="nexttest"><div><a href="generate?test=the-input-byte-stream-009">Next test</a></div><div class="doctype">HTML5</div>
<p class="jump">the-input-byte-stream-007<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-007" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p>
<div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li>
<li>The test is read from a server that supports HTTP.</li></ul></div>
</div>
<script>
test(function() {
assert_equals(document.getElementById('box').offsetWidth, 100);
}, " ");
</script>
</body>
</html>

View File

@ -1,374 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://my.netscape.com/rdf/simple/0.9/">
<channel>
<title>heise online News</title>
<link>https://www.heise.de/newsticker/</link>
<description>Nachrichten nicht nur aus der Welt der Computer</description>
</channel>
<item>
<title>OLED-TVs: Vorsichtsmaßnahmen gegen Einbrennen</title>
<link>https://www.heise.de/newsticker/meldung/OLED-TVs-Vorsichtsmassnahmen-gegen-Einbrennen-4205274.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Wer gerade einen neuen OLED-Fernseher gekauft hat oder sich zu Weihnachten einen zuzulegen möchte, sollte unbedingt ein paar Hinweise beachten.</description>
</item>
<item>
<title>Mega-Deal: IBM übernimmt Red Hat</title>
<link>https://www.heise.de/newsticker/meldung/Mega-Deal-IBM-uebernimmt-Red-Hat-4205582.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Giganten-Hochzeit in den USA: Der Computerkonzern IBM übernimmt den Open-Source-Anbieter Red Hat für umgerechnet 30 Milliarden Euro.</description>
</item>
<item>
<title>Fortnite-Macher: Epic Games soll 15 Milliarden Dollar wert sein</title>
<link>https://www.heise.de/newsticker/meldung/Fortnite-Macher-Epic-Games-soll-15-Milliarden-Dollar-wert-sein-4205522.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Epic Games konnte bei einer Investionsrunde einige neue Geldgeber von sich überzeugen. Insgesamt flossen 1,25 Milliarden US-Dollar.</description>
</item>
<item>
<title>Erster nichtstaatlicher Raketenstart in China fehlgeschlagen</title>
<link>https://www.heise.de/newsticker/meldung/Erster-nichtstaatlicher-Rekatenstart-in-China-fehlgeschlagen-4205524.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Trägerrakete ZQ-1 hat es wegen unbekannter technischer Probleme nach dem Start nicht in die Erdumlaufbahn geschafft.</description>
</item>
<item>
<title>eARC: Immer mehr Hersteller schalten HDMI-Audio-Rückkanal frei</title>
<link>https://www.heise.de/newsticker/meldung/eARC-Hersteller-schalten-HDMI-Audio-Rueckkanal-frei-4205518.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Während andere HDMI-2.1-Funktionen auf sich warten lassen, ist der &quot;enhanced Audio Return Channel&quot; nach einem Firmware-Update schon bei AV-Receivern nutzbar.</description>
</item>
<item>
<title>Vorschau: Neue PC-Spiele im November 2018</title>
<link>https://www.heise.de/newsticker/meldung/Vorschau-Neue-PC-Spiele-im-November-2018-4202098.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Jeden Monat schicken Spiele-Hersteller zahlreiche neue Titel ins Rennen. Wir haben die wichtigsten Spiele-Neuerscheinungen im November herausgesucht.</description>
</item>
<item>
<title>Israelisches Start-up baut faltbares Elektroauto</title>
<link>https://www.heise.de/newsticker/meldung/Israelisches-Start-up-baut-faltbares-Elektroauto-4205501.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das zweisitzige Auto kann sein Fahrgestell zum Parken einklappen und passt dann auf einen Motorradparkplatz.</description>
</item>
<item>
<title>Flash-Speicher: WD will Produktion einschränken</title>
<link>https://www.heise.de/newsticker/meldung/Flash-Speicher-WD-will-Produktion-einschraenken-4205498.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Preise für NAND-Flash-Speicher kennen derzeit nur eine Richtung: abwärts. WD will dem mit Produktionseinschränkungen begegnen.</description>
</item>
<item>
<title>LED-Tastatur Aukey KM-G6 im Test: mechanisch, günstig und laut</title>
<link>https://www.techstage.de/news/Mechanische-Tastatur-Aukey-KM-G6-im-Test-guenstig-und-laut-4205068.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Aukey KM-G6 kostet weniger als 50 Euro und zeigt, dass mechanische Tastaturen nicht teuer sein müssen. Wir testen das Keyboard.</description>
</item>
<item>
<title>Einhörner zum Leben erwecken - Kultur-Hackathon in Mainz gestartet</title>
<link>https://www.heise.de/newsticker/meldung/Einhoerner-zum-Leben-erwecken-Kultur-Hackathon-in-Mainz-gestartet-4205490.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>In Museen, Bibliotheken und Archive stecken viele Daten, die sich kreativ nutzen lassen. Programmierer, Designer und Historiker haben sich das vorgenommen.</description>
</item>
<item>
<title>Impressionen von der SPIEL 2018: Gesellschaftsspiele für Nerds und Geeks</title>
<link>https://www.heise.de/newsticker/meldung/Impressionen-von-der-SPIEL-2018-Gesellschaftsspiele-fuer-Nerds-und-Geeks-4205405.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Weg von Bildschirm und Controller, hin zu Würfeln und Karten. Die SPIEL-Messe zeigt Neuheiten bei IT-affinen Brett-, Karten- und Tabletop-Spielen.</description>
</item>
<item>
<title>Missing Link: Vor 100 Jahren begann die deutsche Revolution</title>
<link>https://www.heise.de/newsticker/meldung/Missing-Link-Vor-100-Jahren-begann-die-deutsche-Revolution-4205422.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Von den Sturmvögeln zu den Stiefkindern der Revolution: Mit dem Matrosenaufstand startete Deutschland in die erste Republik.</description>
</item>
<item>
<title>4W: Was war. Was wird. Wettrüsten oder Waffelessen, das ist die Frage.</title>
<link>https://www.heise.de/newsticker/meldung/Was-war-Wettruesten-oder-Waffelessen-das-ist-die-Frage-4205432.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Zeit! Irgendwann gab es sie gar nicht, heute wird sie uns geschenkt. Ja, auch Hal Faber weiß, dass das Quatsch ist. Wie so vieles andere.</description>
</item>
<item>
<title>Kommentar: Vom DNS, aktuellen Hypes, Überwachung und Zensur</title>
<link>https://www.heise.de/newsticker/meldung/Kommentar-Vom-DNS-aktuellen-Hypes-Ueberwachung-und-Zensur-4205380.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das DNS ist gereift, es kann mit den Bedrohungen der Überwachung und Zensur umgehen. Eine Antwort von Lutz Donnerhacke auf &quot;Die Gruft DNS gehört ausgelüftet&quot;.</description>
</item>
<item>
<title>Microsoft will Militär und Geheimdienste beliefern</title>
<link>https://www.heise.de/newsticker/meldung/Microsoft-will-Militaer-und-Geheimdienste-beliefern-4205383.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Microsoft ist trotz Protesten von Mitarbeitern bereit, dem Militär und den Geheimdiensten des Landes KI-Systeme und sonstige Technologien zu verkaufen.</description>
</item>
<item>
<title>Zurück zur &quot;Normalzeit&quot;: Uhren werden (möglichweise zum letzten Mal) um eine Stunde zurückgestellt</title>
<link>https://www.heise.de/newsticker/meldung/Zurueck-zur-Normalzeit-Uhren-werden-moeglichweise-zum-letzten-Mal-um-eine-Stunde-zurueckgestellt-4205376.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Zeitumstellung könnte bald Geschichte sein. Es gibt aber Bereiche, in denen eine Umstellung sehr aufwendig werden könnte.</description>
</item>
<item>
<title>5G: Seehofer fordert Änderung der Vergaberegeln</title>
<link>https://www.heise.de/newsticker/meldung/5G-Seehofer-fordert-Aenderung-der-Vergaberegeln-4205373.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Bundesinnenminister sieht Bewohner ländlicher Gebiete durch die Ausschreibungsregeln für das 5G-Netz benachteiligt und verlangt Nachbesserungen.</description>
</item>
<item>
<title>Ausstellung erinnert an das Lebenswerk von Konrad Zuse</title>
<link>https://www.heise.de/newsticker/meldung/Ausstellung-erinnert-an-das-Lebenswerk-von-Konrad-Zuse-4205359.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>In Hopferau im Ostallgäu stellte Konrad Zuse seine Rechenmaschine Z4 fertig. Jetzt erinnert eine Ausstellung im Schloss Hopferau an den Computerpionier.</description>
</item>
<item>
<title>Test TrackerID LTS-450: GPS-Flaschenhalter am Fahrrad</title>
<link>https://www.techstage.de/news/Test-TrackerID-LTS-450-GPS-Flaschenhalter-am-Fahrrad-4205272.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Fahrraddiebe sind blöd, aber nicht dumm: Sehen sie einen GPS-Tracker, entfernen sie ihn. Deswegen tarnt sich der TrackerID LTS-450 in einem Flaschenhalter.</description>
</item>
<item>
<title>Fünf mobile Beamer im Vergleichstest</title>
<link>https://www.techstage.de/news/Fuenf-mobile-Beamer-im-Vergleichstest-4204823.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>In den vergangenen Wochen haben wir uns fünf kompakte Beamer mit integriertem Akku angeschaut. Im Vergleichstest zeigen wir Vor- und Nachteile.</description>
</item>
<item>
<title>In Japan geht ein weiterer Atomreaktor wieder ans Netz</title>
<link>https://www.heise.de/newsticker/meldung/In-Japan-geht-ein-weiterer-Atomreaktor-wieder-ans-Netz-4205351.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das Atomkraftwerk Ikata in Japan hat einen Reaktor wieder hochgefahren, gegen dessen Betrieb eine Bürgergruppe geklagt hatte.</description>
</item>
<item>
<title>FlyCroTug: Kleine Drohne mit großer Zugkraft</title>
<link>https://www.heise.de/newsticker/meldung/FlyCroTug-Kleine-Drohne-mit-grosser-Zugkraft-4205335.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Forscher haben 100 Gramm leichte Minidrohnen entwickelt, die von einem festen Haltepunkt aus das 40-fache ihres Gewichts bewegen können.
</description>
</item>
<item>
<title>Google Office-Programme: Neue Dokumente in Browserzeile anlegen</title>
<link>https://www.heise.de/newsticker/meldung/Google-Docs-Neue-Dokumente-in-Browserzeile-anlegen-4205346.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Für seine webbasierten Office-Programme hat Google eine praktische Abkürzung direkt in die Anwendungen Docs, Sheets, Slides und Forms veröffentlicht.</description>
</item>
<item>
<title>Wo Hobby-Astronomen den Profis voraus sind</title>
<link>https://www.heise.de/newsticker/meldung/Wo-Hobby-Astronomen-den-Profis-voraus-sind-4205332.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Hobby-Astronomen leisten einen wertvollen Beitrag zur Wissenschaft, etwa durch das Beobachten veränderlicher Sterne oder die Suche nach Meteoriten.</description>
</item>
<item>
<title>Zahlen geschönt? FBI-Untersuchung gegen Tesla</title>
<link>https://www.heise.de/newsticker/meldung/Zahlen-geschoent-FBI-Untersuchung-gegen-Tesla-4205285.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Es besteht der Verdacht, dass Tesla Produktionszahlen wissentlich überoptimistisch vorhergesagt hat. Das FBI ermittelt strafrechtlich.</description>
</item>
<item>
<title>c't uplink 24.6: Linux mit UEFI / OLED-Defekte / Dropbox-Alternativen</title>
<link>https://www.heise.de/ct/artikel/c-t-uplink-24-6-Linux-mit-UEFI-OLED-Defekte-Dropbox-Alternativen-4205070.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die c't-uplink Show: Dieses Mal mit eingebrannten Logos bei OLED-TVs, Alternativen zu Dropbox und wie man Linux am besten mit UEFI verheiratet.</description>
</item>
<item>
<title>Die Bilder der Woche (KW 43): Von Porträt bis Landschaft</title>
<link>https://www.heise.de/foto/meldung/Die-Bilder-der-Woche-KW-43-Von-Portraet-bis-Landschaft-4204550.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Blickfang: Unsere Bilder des Tages haben in dieser Woche keine Scheu vor direktem Augenkontakt und Spiegelbildern.</description>
</item>
<item>
<title>BIOS-Option macht ThinkPads zu Briefbeschwerern</title>
<link>https://www.heise.de/newsticker/meldung/BIOS-Option-macht-ThinkPads-zu-Briefbeschwerern-4205185.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Ein einziger Klick im BIOS-Setup - und einige aktuelle Lenovo-Notebooks starten nicht mehr; eine Lösung des Problems fehlt noch.</description>
</item>
<item>
<title>Markenstreit um “Dash”: Bragi beantragt einstweilige Verfügung gegen Oneplus</title>
<link>https://www.heise.de/newsticker/meldung/Markenstreit-um-Dash-Bragi-beantragt-einstweilige-Verfuegung-gegen-Oneplus-4205223.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Hersteller der smarten Kopfhörer “The Dash” sieht seine Markenrechte durch die Schnelladegeräte der chinesischen Smartphones verletzt.</description>
</item>
<item>
<title>Übernahme von GitHub durch Microsoft abgeschlossen</title>
<link>https://www.heise.de/newsticker/meldung/Uebernahme-von-GitHub-durch-Microsoft-abgeschlossen-4205119.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Microsofts Übernahme von GitHub ist abgeschlossen. Ab Montag beginnt die Arbeit des neuen CEO Nat Friedman. Er will GitHub für die Entwickler besser machen.</description>
</item>
<item>
<title>Webhosting und Cloud Computing: Aus 1&amp;1 und ProfitBricks wird 1&amp;1 Ionos</title>
<link>https://www.heise.de/newsticker/meldung/Webhosting-und-Cloud-Computing-Aus-1-1-und-ProfitBricks-wird-1-1-Ionos-4205066.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>1&amp;1 bietet seine Webhosting- und Cloud-Produkte künftig unter dem Namen 1&amp;1 Ionos an. Besserer Service soll Unternehmen den Cloud-Start schmackhaft machen.</description>
</item>
<item>
<title>iPhone XR: Die 10 wichtigsten Testergebnisse</title>
<link>https://www.heise.de/mac-and-i/meldung/iPhone-XR-Die-10-wichtigsten-Testergebnisse-4204845.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Seit heute ist das dritte und günstigste von Apples neuen Smartphones im Handel. Mac &amp; i konnte es bereits testen.</description>
</item>
<item>
<title>Motorola One: Android-One-Smartphone für 299 Euro im Test</title>
<link>https://www.techstage.de/news/Motorola-One-im-Test-Android-One-Smartphone-mit-Notch-4203618.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das Motorola One ist ein Smartphone mit schickem Design und Android One als Betriebssystem. Ob und für wen sich der Kauf lohnt, hat TechStage getestet.</description>
</item>
<item>
<title>US Copyright Office: DRM darf für Reparaturen umgangen werden</title>
<link>https://www.heise.de/newsticker/meldung/US-Copyright-Office-DRM-darf-fuer-Reparaturen-umgangen-werden-4205173.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>In den USA ist es künftig legal, den Kopierschutz elektronischer Geräte zu knacken, um etwa sein Smartphone, Auto oder den vernetzten Kühlschrank zu reparieren.</description>
</item>
<item>
<title>Ausprobiert: Haptik-Datenhandschuhe von Senseglove</title>
<link>https://www.heise.de/newsticker/meldung/Ausprobiert-Haptik-Datenhandschuhe-von-Senseglove-4205142.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Senseglove-Datenhandschuh trackt nicht nur jeden Finger, sondern simuliert auch Widerstand und Haptik. Wir haben ihn ausprobiert.</description>
</item>
<item>
<title>Apple News soll &quot;Netflix für Nachrichten&quot; werden</title>
<link>https://www.heise.de/mac-and-i/meldung/Apple-News-soll-Netflix-fuer-Nachrichten-werden-4204886.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Apple betreibt für seinen hauseigenen Infodienst eine eigene Redaktion und setzt kaum auf Algorithmen. Im Abo könnten bald diverse Magazine hinzukommen.</description>
</item>
<item>
<title>Xbox One X im 4K-Test: Spaßzentrale für UHD-TVs</title>
<link>https://www.techstage.de/news/Xbox-One-X-im-4K-Test-Spasszentrale-fuer-UHD-TVs-4204803.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die aktuelle Xbox One X bewirbt Microsoft als idealen Zuspieler für UHD-Fernseher. Wir haben ihre 4K-Fähigkeiten bei Filmen und Spielen getestet. </description>
</item>
<item>
<title>Red Dead Redemption 2: Die Entschleunigung des Action-Adventures</title>
<link>https://www.heise.de/newsticker/meldung/Red-Dead-Redemption-2-Die-Entschleunigung-des-Action-Adventures-4205034.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Nach dem Live-Stream: Unsere Eindrücke aus den ersten Stunden des Gaming-Blockbusters Red Dead Redemption 2.</description>
</item>
<item>
<title>Grüne: Netzbetreiber sollen Breitband-Universaldienst finanzieren</title>
<link>https://www.heise.de/newsticker/meldung/Gruene-Netzbetreiber-sollen-Breitband-Universaldienst-finanzieren-4205022.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Schwarz-Rot will bis spätestens 2025 einen Anspruch auf Breitband für alle gesetzlich verankern. Die Grünen sehen dagegen sofortigen Handlungsbedarf.</description>
</item>
<item>
<title>Programmiersprache: Rust 1.30 will mehr Klarheit schaffen</title>
<link>https://www.heise.de/developer/meldung/Programmiersprache-Rust-1-30-will-mehr-Klarheit-schaffen-4204893.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Umgang mit absoluten Pfaden und neue prozedurale Makros sind die Highlights der aktuellen Rust-Version.</description>
</item>
<item>
<title>Apples Oktober-Event: iPad Pro 2018 und neue Macs vor der Tür</title>
<link>https://www.heise.de/mac-and-i/meldung/Apples-Oktober-Event-iPad-Pro-2018-und-neue-Macs-vor-der-Tuer-4204922.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Nach der Einführung der iPhones wird sich Apple der Zukunft seiner Computer zuwenden: Wichtige Neuerungen stehen an. </description>
</item>
<item>
<title>Magento-Shops: Verwundbare Add-ons als Schlupfloch für Kreditkarten-Skimmer</title>
<link>https://www.heise.de/security/meldung/Magento-Shops-Verwundbare-Add-ons-als-Schlupfloch-fuer-Kreditkarten-Skimmer-4204828.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Ein Sicherheitsforscher warnt vor knapp über 20 Add-ons, die Onlineshops basierend auf der Magento-Software angreifbar machen. </description>
</item>
<item>
<title>Atomkraft: Gericht kippt Schließungs-Dekret für Fessenheim</title>
<link>https://www.heise.de/newsticker/meldung/Atomkraft-Gericht-kippt-Schliessungs-Dekret-fuer-Fessenheim-4204853.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Conseil d'État hat zu Gunsten der Gemeinde Fessenheim und der Gewerkschaften entschieden.</description>
</item>
<item>
<title>Qt Design Studio erreicht Version 1.0</title>
<link>https://www.heise.de/developer/meldung/Qt-Design-Studio-erreicht-Version-1-0-4204902.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Neue Funktionen wie die Qt Photoshop Bridge und zeitachsenbasierte Animationen sollen die Zusammenarbeit von Entwicklern und Designern vereinfachen.</description>
</item>
<item>
<title>Vodafone bringt Mini-Handy von Palm nach Deutschland</title>
<link>https://www.heise.de/newsticker/meldung/Vodafone-bringt-Mini-Handy-von-Palm-nach-Deutschland-4204878.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das neue Palm kommt auch in Deutschland auf den Markt: Vodafone bringt das kuriose Mini-Handy exklusiv in den Handel. </description>
</item>
<item>
<title>Xeons und Modems bescheren Intel Rekordquartal</title>
<link>https://www.heise.de/newsticker/meldung/Xeons-und-Modems-bescheren-Intel-Rekordquartal-4204869.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Starke Nachfrage nach Prozessoren für Cloud-Rechenzentren sowie LTE-Modems lassen bei Intel die Gewinne sprudeln und bescheren gute Aussichten.</description>
</item>
<item>
<title>KI druckt Kunst: Auktionshaus Christie's versteigert KI-Gemälde für 380.000 Euro</title>
<link>https://www.heise.de/newsticker/meldung/KI-druckt-Kunst-Auktionshaus-Christie-s-versteigert-KI-Gemaelde-fuer-380-000-Euro-4204793.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Das renommierte Auktionshaus Christie's hat erstmals ein von einer KI erschaffenes Bild versteigert. Der Preis war wesentlich höher als erwartet.</description>
</item>
<item>
<title>EU-Parlament verabschiedet Resolution zur Datenschutzuntersuchung bei Facebook </title>
<link>https://www.heise.de/newsticker/meldung/EU-Parlament-verabschiedet-Resolution-zur-Datenschutzuntersuchung-bei-Facebook-4204766.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Facebook soll mehr Aufklärung über seine Datenschutzpraxis leisten. Das EU-Parlament hat deshalb eine Untersuchung durch EU-Institutionen verabschiedet.</description>
</item>
<item>
<title>IBM setzt auf 277.000 Apple-Geräte</title>
<link>https://www.heise.de/mac-and-i/meldung/IBM-setzt-auf-277-000-Apple-Geraete-4204728.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Bei IBM stellen Macs inzwischen ein Viertel aller Laptops. Das Open-Source-Tool Mac@IBM soll auch Admins anderer Firmen die Einrichtung erleichtern.</description>
</item>
<item>
<title>heise-Angebot: #TGIQF - das c't-Retroquiz: 8 Bit &amp; mehr</title>
<link>https://www.heise.de/newsticker/meldung/TGIQF-das-c-t-Retroquiz-8-Bit-mehr-4202717.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>C64, ZX Spectrum, Apple II ... die Heimcomputer lösten eine IT-Revolution in den Kinderzimmern aus. Wie viel ist aus der Zeit bei Ihnen hängen geblieben?</description>
</item>
<item>
<title>heise-Angebot: c't Fotografie: Spiegeloses Vollformat im Test</title>
<link>https://www.heise.de/foto/meldung/c-t-Fotografie-Spiegeloses-Vollformat-im-Test-4201826.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Lange war Sony der einzige Anbieter für spiegellose Vollformatkameras. Mit der Canon EOS R und der Nikon Z-Serie werden die Karten neu gemischt.</description>
</item>
<item>
<title>British-Airways-Hack: 185.000 weitere Kunden betroffen</title>
<link>https://www.heise.de/newsticker/meldung/British-Airways-Hack-185-000-weitere-Kunden-betroffen-4204675.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Fluggesellschaft hat nun bekanntgegeben, dass bis dato Unbekannte Kreditkartendaten von noch mehr Kunden als bislang bekannt kopiert haben.</description>
</item>
<item>
<title>Google: 48 Mitarbeiter wegen sexueller Belästigung gefeuert</title>
<link>https://www.heise.de/newsticker/meldung/Google-48-Mitarbeiter-wegen-sexueller-Belaestigung-gefeuert-4204687.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Der Weggang von Android-Schöpfer Andy Rubin von Google war wohl nicht freiwillig ihm wurde sexuelle Nötigung vorgeworfen. Und er war nicht der einzige.</description>
</item>
<item>
<title>Fujitsu schließt Werk in Augsburg</title>
<link>https://www.heise.de/newsticker/meldung/Fujitsu-schliesst-Werk-in-Augsburg-4204722.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Fujitsu plant einen Konzernumbau. In Augsburg sind davon 1500 Beschäftigte betroffen.</description>
</item>
<item>
<title>Ego-Shooter Metro 2033 bei Steam kostenlos</title>
<link>https://www.heise.de/newsticker/meldung/Metro-2033-ist-bei-Steam-heute-kostenlos-4204706.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Metro 2033 wird am heutigen Freitag auf Steam kostenlos angeboten. Der Ego-Shooter basiert auf dem gleichnamigen Roman von Dmitri Gluchowski.</description>
</item>
<item>
<title>Lightroom-Alternative: DxO bringt PhotoLab 2 </title>
<link>https://www.heise.de/foto/meldung/Lightroom-Alternative-DxO-bringt-PhotoLab-2-4204614.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>PhotoLab gibt es nun in Version 2: Verbessert wurde unter anderem die Bildverwaltung, zudem integriert DxO die von den Nik-Filtern bekannte U-Point-Technologie.</description>
</item>
<item>
<title>Google schaltet Nearby Notifcations in Android ab</title>
<link>https://www.heise.de/developer/meldung/Google-schaltet-Nearby-Notifcations-in-Android-ab-4204667.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Die Funktion für standortbasierte Benachrichtigungen lieferte wohl mehr Spam als nützliche Inhalte.</description>
</item>
<item>
<title>iPhone XR: Verkaufsstart ohne Ansturm</title>
<link>https://www.heise.de/mac-and-i/meldung/iPhone-XR-Verkaufsstart-ohne-Ansturm-4204679.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Apple bringt die billigeren und bunten iPhone-Modellreihe in den Handel. Groß anstehen mussten Kunden dafür nicht.
</description>
</item>
<item>
<title>Snapchat: Aktienabsturz durch Nutzerschwund</title>
<link>https://www.heise.de/newsticker/meldung/Snapchat-Aktienabsturz-durch-Nutzerschwund-4204631.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Snapchat laufen weiterhin die Nutzer weg und das wird sich vorerst nicht ändern, sagt Snap. Die Aktie stürzte trotz geringerer Verluste um zehn Prozent ab.</description>
</item>
<item>
<title>Mi Mix 3: Xiaomi-Flaggschiff mit Kamera-Slider und 10 GByte RAM</title>
<link>https://www.heise.de/newsticker/meldung/Mi-Mix-3-Xiaomi-Flaggschiff-mit-Kamera-Slider-und-10-GByte-RAM-4204655.html?wt_mc=rss.ho.beitrag.rdf</link>
<description>Xiaomis nächstes Flaggschiff bietet eine fast randlose Display-Front. Die Selfie-Kamera ist in einem magnetischen Slider-Mechanismus untergebracht.</description>
</item>
</rdf:RDF>

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
<?xml version="1.0"?>

View File

@ -1,226 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<title><![CDATA[BBC News اردو - پاکستان کے لیے امریکی امداد کی بہار و خزاں]]></title>
<description><![CDATA[BBC News اردو - پاکستان کے لیے امریکی امداد کی بہار و خزاں]]></description>
<link>http://www.bbcurdu.com</link>
<image>
<url>http://www.bbc.co.uk/urdu/images/gel/rss_logo.gif</url>
<title>BBC News اردو - پاکستان کے لیے امریکی امداد کی بہار و خزاں</title>
<link>http://www.bbcurdu.com</link>
</image>
<generator>RSS for Node</generator>
<lastBuildDate>Wed, 24 Oct 2018 07:25:10 GMT</lastBuildDate>
<copyright><![CDATA[کاپی رائٹ بی بی سی ]]></copyright>
<language><![CDATA[ur]]></language>
<managingEditor><![CDATA[urdu@bbc.co.uk]]></managingEditor>
<ttl>15</ttl>
<item>
<title><![CDATA[امریکی عسکری امداد کی بندش کی وجوہات: انڈیا سے جنگ، جوہری پروگرام اور اب دہشت گردوں کی پشت پناہی]]></title>
<description><![CDATA[امریکہ اور پاکستان کے 70 سالہ تعلقات میں جب بھی زیادہ کشیدگی آتی ہے تو اس میں پہلی تلوار پاکستان کو ملنے والی عسکری امداد پر چلتی ہے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42575603</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42575603</guid>
<pubDate>Fri, 05 Jan 2018 16:51:00 GMT</pubDate>
<media:thumbnail width="1024" height="576" url="http://c.files.bbci.co.uk/A787/production/_99478824_gettyimages-856735580.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ کے ساتھ خفیہ معلومات کا تبادلہ اور فوجی تعاون معطل کر دیا: وزیر دفاع]]></title>
<description><![CDATA[پاکستان کے وزیر دفاع خرم دستگیر نے کہا ہے کہ امریکہ کی جانب سے عسکری امداد کی معطلی کے بعد پاکستان نے امریکہ سے خفیہ معلومات کا تبادلہ اور فوجی تعاون بند کر دیا ہے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42645212</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42645212</guid>
<pubDate>Thu, 11 Jan 2018 13:20:55 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/13C09/production/_99550908_3f6467e7-5086-43e5-9c31-918be66a17ad.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان ان دہشت گرد گروہوں کے خلاف کارروائی کرے جن کے خلاف ہم چاہتے ہیں: امریکہ]]></title>
<description><![CDATA[امریکی محکمۂ دفاع کا کہنا ہے کہ امریکہ چاہتا ہے کہ پاکستان دہشت گردوں کے خلاف فیصلہ کن کارروائی کرے اور یہ کہ امریکہ کو بعض معاملات پر شدید اختلافات ہیں اور ان پر کام کیا جا رہا ہے۔]]></description>
<link>http://www.bbc.com/urdu/world-42615276</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/world-42615276</guid>
<pubDate>Tue, 09 Jan 2018 02:59:43 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/5B55/production/_99518332_mediaitem99518331.jpg"/>
</item>
<item>
<title><![CDATA[پاکستانی وزیر دفاع کہتے ہیں کہ امریکہ کو ایک کامیابی ملی ہے، وہ بھی پاکستان کی مرہون منت]]></title>
<description><![CDATA[پاکستان کے وزیر دفاع خرم دستگیر نے کہا ہے کہ افغانستان کی صورتحال کی تمام ذمہ داری پاکستان پر نہیں ڈالی جا سکتی۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42554318</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42554318</guid>
<pubDate>Wed, 03 Jan 2018 15:50:55 GMT</pubDate>
<media:thumbnail width="1024" height="576" url="http://c.files.bbci.co.uk/16765/production/_99450029_p05snlw4.jpg"/>
</item>
<item>
<title><![CDATA[صدر ٹرمپ کے ٹویٹ کو سنجیدگی سے لیتے ہیں: وزیر دفاع]]></title>
<description><![CDATA[پاکستان کے وزیر دفاع خرم دستگیر نے کہا کہ پاکستان امریکی صدر ٹرمپ کے پاکستان کے بارے میں ٹویٹ کو سنجیدگی سے لیتے ہیں اور تہذیب کے دائرے میں رہتے ہوئے امریکہ سے بے لاگ بات ہو گی۔]]></description>
<link>http://www.bbc.com/urdu/42547605</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/42547605</guid>
<pubDate>Tue, 02 Jan 2018 17:27:36 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/5AA4/production/_99440232_p05sk783.jpg"/>
</item>
<item>
<title><![CDATA[امریکی وزیرِ خارجہ کی پاکستان آمد]]></title>
<description><![CDATA[امریکی وزیرِ خارجہ ریکس ٹلرسن نے پاکستان آمد کے بعد وزیراعظم ہاؤس میں پاکستان کی اعلیٰ سول اور فوجی قیادت سے ملاقات کی۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41739055</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41739055</guid>
<pubDate>Tue, 24 Oct 2017 13:08:06 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/10293/production/_98459166_p05ktkrj.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ اور پاکستان ایک دوسرے کا کیا بگاڑ سکتے ہیں؟]]></title>
<description><![CDATA[تجزیہ کار کہتے ہیں کہ حقیقتاً افغانستان میں پاکستان اور امریکہ دونوں ہی ناکام ہوئے ہیں اور دونوں یہ سمجھتے ہیں کہ دوسرے کو شکست دے کر وہ پورے افغانستان پر اثر و رسوخ قائم کر سکیں گے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42542988</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42542988</guid>
<pubDate>Tue, 02 Jan 2018 16:28:18 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/1AED/production/_99439860_gettyimages-839854052.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ اور پاکستان کے کشیدہ تعلقات میں اربوں ڈالر کی امداد پر بھی تنازع]]></title>
<description><![CDATA[پاکستان اور امریکہ کے کشیدہ تعلقات میں اربوں ڈالر کی امداد پر بھی تنازع ہے جس میں دونوں ممالک الگ الگ اعداد و شمار بتاتے ہیں۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42532582</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42532582</guid>
<pubDate>Tue, 02 Jan 2018 10:28:02 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/9281/production/_99050573__98456547_d3ee46a1-51a1-48b1-a24f-9a41cf21361e.jpg"/>
</item>
<item>
<title><![CDATA[’امریکہ انسداد دہشتگردی سیکھنے کے بجائے دشنام طرازی کر رہا ہے‘]]></title>
<description><![CDATA[پاکستان کے وزیر دفاع خرم دستگیر خان نے کہا ہے کہ پاکستان میں دہشت گردوں کی خفیہ پناہ گاہیں نہیں ہیں اور اگر باقیات ہیں تو انہیں رد الفساد کے تحت ختم کیا جا رہا ہے تاکہ پاکستان کا مستقبل محفوظ ہو سکے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42548010</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42548010</guid>
<pubDate>Wed, 03 Jan 2018 05:04:13 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/9B36/production/_99443793_image1.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان بھی ’مذہبی آزادیوں کی خلاف ورزیاں‘ کرنے والے ممالک میں شامل]]></title>
<description><![CDATA[امریکہ کی وزارتِ خارجہ نے پاکستان کا نام ان ملکوں کی فہرست میں شامل کر دیا ہے جہاں مبینہ طور پر مذہبی آزادیوں کی یا تو سنگین خلاف ورزیوں کا ارتکاب کیا جاتا ہے یا مذہبی آزادیوں پر پابندیاں عائد ہیں۔]]></description>
<link>http://www.bbc.com/urdu/world-42571559</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/world-42571559</guid>
<pubDate>Thu, 04 Jan 2018 17:12:58 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/184A8/production/_99469499_gettyimages-894786806.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان کی سکیورٹی امداد معطل: ’دہشت گردی کے خلاف عزم پر اثرانداز نہیں ہو سکتی‘]]></title>
<description><![CDATA[امریکہ کی طرف سے پاکستان کی فوجی امداد کے بند کیے جانے پر پاکستان کے دفتر خارجہ نے کہا ہے کہ یک طرفہ بیانات، مرضی سے دی گئی ڈیڈ لائنز اور اہداف کی مستقل تبدیلی مشترکہ مفادات کے حصول میں سودمند ثابت نہیں ہو سکتی۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42577314</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42577314</guid>
<pubDate>Fri, 05 Jan 2018 12:32:27 GMT</pubDate>
<media:thumbnail width="640" height="360" url="http://c.files.bbci.co.uk/0935/production/_99475320_556f3020-6286-47a8-a4b1-3026ff6dc95f.jpg"/>
</item>
<item>
<title><![CDATA[ڈونلڈ ٹرمپ: پاکستان نے ہمیں جھوٹ اور دھوکے کے سوا کچھ نہیں دیا]]></title>
<description><![CDATA[امریکی صدر ڈونلڈ ٹرمپ کا کہنا ہے کہ گذشتہ15 برس میں 33 ارب ڈالر کی امداد لینے کے باوجود پاکستان نے امریکہ کو سوائے جھوٹ اور دھوکے کے کچھ نہیں دیا۔]]></description>
<link>http://www.bbc.com/urdu/world-42534486</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/world-42534486</guid>
<pubDate>Mon, 01 Jan 2018 17:59:24 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/A5CE/production/_99264424_gettyimages-894923566.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان: اتحادی ایک دوسرے کو تنبیہ جاری نہیں کیا کرتے]]></title>
<description><![CDATA[امریکی نائب صدر مائیک پینس کے پاکستان کے بارے میں بگرام کے ہوائی اڈے پر دیے جانے والے بیان پر تبصرہ کرتے ہوئے پاکستان کی وزارتِ خارجہ کے ترجمان نے کہا کہ یہ بیان امریکی انتظامیہ کے ساتھ ہونے والے تفصیلی مذاکرات کے منافی ہے۔]]></description>
<link>http://www.bbc.com/urdu/world-42451883</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/world-42451883</guid>
<pubDate>Fri, 22 Dec 2017 08:50:54 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/12825/production/_99331857_gettyimages-896767012.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ: کون سے ممالک ہیں جن پر امداد بند کر دینے کی دھمکی کارگر ثابت نہیں ہوئی]]></title>
<description><![CDATA[سب سے زیادہ امریکی امداد وصول کرنے والے 12 ملکوں کی فہرست میں اسرائیل کے علاوہ ایک بھی ملک ایسا نہیں جس نے جنرل اسمبلی میں یروشلم کے بارے میں امریکی صدر کے فیصلے کے خلاف قرارداد کی مخالفت میں ووٹ دیا ہو۔]]></description>
<link>http://www.bbc.com/urdu/world-42457273</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/world-42457273</guid>
<pubDate>Fri, 22 Dec 2017 15:29:44 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/1331F/production/_99332687_gettyimages-882119996.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان امریکہ تعلقات، بیان بدلتے ہیں لیکن عینک نہیں]]></title>
<description><![CDATA[پاکستان اور امریکہ کے نرم گرم تعلقات کی تاریخ صرف افغانستان تک محدود نہیں، لیکن حالیہ برسوں میں دونوں اکثر ایک دوسرے کو ایک ہی عینک سے دیکھنے کی کوشش کرتے ہیں۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42225606</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42225606</guid>
<pubDate>Mon, 04 Dec 2017 13:36:14 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/9281/production/_99050573__98456547_d3ee46a1-51a1-48b1-a24f-9a41cf21361e.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان امریکہ تعلقات: ’باتیں دھمکی کی زبان سے نہیں صلح کی زبان سے طے ہوں گی‘]]></title>
<description><![CDATA[پاکستان کے وزیر خارجہ خواجہ آصف نے کہا ہے کہ امریکہ اور پاکستان کے درمیان اعتماد کا فقدان راتوں رات ختم نہیں ہو گا کیونکہ دونوں ممالک کے تعلقات پر جمی برف پگھلنے میں وقت لگے گا۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41742387</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41742387</guid>
<pubDate>Wed, 25 Oct 2017 01:19:34 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/15BE/production/_98466550_844059008.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ کے ساتھ تعاون ختم کرنے پر غور کیا جائے: قرارداد]]></title>
<description><![CDATA[پاکستان کی پارلیمان نے ایک متفقہ قرارداد میں امریکی صدر کی حالیہ تقریراور افغانستان میں امریکی کمانڈر جنرل جان نکلسن کے بیان کو مسترد کر دیا ہے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41097529</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41097529</guid>
<pubDate>Wed, 30 Aug 2017 16:19:55 GMT</pubDate>
<media:thumbnail width="640" height="360" url="http://c.files.bbci.co.uk/8C50/production/_97602953_3ad0abf1-2480-41b0-bc0a-9d89393c71e4.jpg"/>
</item>
<item>
<title><![CDATA[’پاکستان کو قربانی کا بکرا بناکر افغانستان میں امن نہیں لایا جاسکتا‘]]></title>
<description><![CDATA[پاکستان کی اعلیٰ سیاسی اور عسکری قیادت نے امریکی صدر ڈونلڈ ٹرمپ کی جانب سے اپنے حالیہ خطاب میں پاکستان پر لگائے گئے الزامات کو مسترد کرتے ہوئے کہا ہے کہ پاکستان پر الزام تراشیوں سے افغانستان کو مستحکم نہیں کیا جا سکتا۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41038882</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41038882</guid>
<pubDate>Thu, 24 Aug 2017 14:35:50 GMT</pubDate>
<media:thumbnail width="640" height="360" url="http://c.files.bbci.co.uk/38EA/production/_97507541_gettyimages-831217164.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ سے امداد کے نہیں اعتماد کے خواہاں ہیں: جنرل باجوہ]]></title>
<description><![CDATA[پاکستان کے آرمی چیف قمر جاوید باجوہ نے کہا ہے کہ پاکستان امریکہ سے کسی مادی یا مالی امداد کا خواہاں نہیں بلکہ چاہتا ہے کہ اس پر اعتماد کرتے ہوئے اس کی کارکردگی کا اعتراف کیا جائے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41024731</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41024731</guid>
<pubDate>Wed, 23 Aug 2017 13:11:20 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/E6B3/production/_97495095_dh6cwc3xuaawzfm.jpg"/>
</item>
<item>
<title><![CDATA[’پاکستان نے اپنے رویے کو تبدیل نہ کیا تو امریکی مراعات کھو سکتا ہے‘]]></title>
<description><![CDATA[امریکی وزیر خارجہ ریکس ٹیلرسن نے افغان طالبان کی مبینہ حمایت پر کہا ہے کہ اگر پاکستان اپنے رویے میں تبدیلی لانے میں ناکام رہتا ہے تو امریکی مراعات کھو سکتا ہے جبکہ پاکستان نے کہا ہے کہ امریکہ محفوظ پناہ گاہوں کے جھوٹے بیانیے کے بجائے دہشت گردی کے خلاف مل کر کام کرے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-41019799</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41019799</guid>
<pubDate>Tue, 22 Aug 2017 23:06:07 GMT</pubDate>
<media:thumbnail width="640" height="360" url="http://c.files.bbci.co.uk/1710E/production/_97487449_gettyimages-825293218.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ کا پاکستان کو ’نوٹس‘ کیا اور کتنا سنگین ہے؟]]></title>
<description><![CDATA[امریکی صدر ڈونلڈ ٹرمپ نے ٹویٹ کے ذریعے اپنے ارادے تو ظاہر کر دیے ہیں لیکن دیکھنا یہ ہے کیا امریکہ کی دھمکی محض افغان طالبان تک محدود ہے یا پھر انڈیا مخالف گروپس بھی اس میں شامل ہوں گے۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42550677</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42550677</guid>
<pubDate>Wed, 03 Jan 2018 07:47:12 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/14B12/production/_99445748_18215753-eb0c-4c5d-b4b7-06aa87bfcd39.jpg"/>
</item>
<item>
<title><![CDATA[کمال ہے، اتنی دہشت؟]]></title>
<description><![CDATA[مجھے تو تینتیس ارب ڈالر کے پاکستانی روپے ہی بنانے نہیں آ ت ، بھیا ٹرمپ! ہم سے حساب کیا مانگتے ہو؟ جن کو دیا تھا صاف صاف ان کا نام لو یا تم بھی غائب ہونے سے ڈرتے ہو؟]]></description>
<link>http://www.bbc.com/urdu/pakistan-42594820</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42594820</guid>
<pubDate>Sun, 07 Jan 2018 03:49:01 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/5F27/production/_99495342_cf7d6805-64db-4438-8fe7-85fd61e470cf.jpg"/>
</item>
<item>
<title><![CDATA[کیا امداد بند کرنے سے امریکہ کا مقصد پورا ہو گا؟]]></title>
<description><![CDATA[امریکہ کی جانب سے پاکستان کی عسکری امداد بند کیے جانے پر ماہرین کا کہنا ہے کہ اس اقدام سے کمزور اقتصادی صورتحال میں پاکستان پر دباؤ پڑے گا اور دہشت گردی کے خلاف جنگ بھی متاثر ہو گئی۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42575493</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42575493</guid>
<pubDate>Fri, 05 Jan 2018 08:27:04 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/D4AF/production/_99474445_gettyimages-482348300.jpg"/>
</item>
<item>
<title><![CDATA[وہ تازیانے لگے ہوش سب ٹھکانے لگے]]></title>
<description><![CDATA[وہ قوم جو 30 برس پہلے تک ناک پے مکھی نہیں بیٹھنے دیتی تھی اس کا یہ حال ہوگیا کہ چابک چھوڑ چابک کے سائے سے بھی ڈر جاتی ہے۔]]></description>
<link>http://www.bbc.com/urdu/42596931</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/42596931</guid>
<pubDate>Sun, 07 Jan 2018 12:37:26 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/863B/production/_99036343_batsebat.jpg"/>
</item>
<item>
<title><![CDATA[امریکہ پاکستان سے چاہتا کیا ہے؟]]></title>
<description><![CDATA[امریکی وزیر خارجہ ریکس ٹلرسن پاکستان کے مختصر دورے پر اسلام آباد پہنچ گئے جہاں وہ چند ’مخصوص‘ مطالبات بھی پیش کریں گے۔ آخر امریکہ پاکستان سے چاہتا کیا ہے اور یہ مطالبات کیا ہو سکتے ہیں؟]]></description>
<link>http://www.bbc.com/urdu/pakistan-41736761</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-41736761</guid>
<pubDate>Tue, 24 Oct 2017 12:53:37 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/12345/production/_98456547_d3ee46a1-51a1-48b1-a24f-9a41cf21361e.jpg"/>
</item>
<item>
<title><![CDATA[پاکستان امریکہ تعلقات میں ’ڈو مور‘ کا نیا ایڈیشن جو آج تک چل رہا]]></title>
<description><![CDATA[افغانستان میں بین الاقوامی سیاست اور ترجیحات سمجھنے کے لیے یہ جاننا ضروری ہے کہ 2001 میں امریکی آمد کے بعد سے بعض ایسی چیزیں ہیں جو تبدیل نہیں ہو رہیں۔]]></description>
<link>http://www.bbc.com/urdu/pakistan-42422392</link>
<guid isPermaLink="true">http://www.bbc.com/urdu/pakistan-42422392</guid>
<pubDate>Wed, 20 Dec 2017 12:23:23 GMT</pubDate>
<media:thumbnail width="976" height="549" url="http://c.files.bbci.co.uk/7CB9/production/_99292913_gettyimages-884411870.jpg"/>
</item>
</channel>
</rss>

View File

@ -1,242 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<meta name="keywords" content="bash.org.ru, iBASH.org.ru, цитатник рунета" />
<meta name="description" content="ibash.org.ru — Новый цитатник Рунета" />
<link href="/favicon.ico" rel="shortcut icon" />
<link href="/css.css" rel="stylesheet" type="text/css" />
<link href="http://ibash.org.ru/rss.xml" rel="alternate" type="application/rss+xml" />
<title>ibash.org.ru — Новый цитатник Рунета</title>
<script type="text/javascript" src="/voting.js"></script>
</head>
<body>
<div class="header">
<h1>ibash.org.ru — Новый цитатник Рунета</h1>
</div>
<div class="menu">
[&nbsp;По дате&nbsp;] [&nbsp;<a href="/best.php">По рейтингу</a>&nbsp;] [&nbsp;<a href="/random.php">Случайно</a>&nbsp;] [&nbsp;<a href="/add.php">Добавить цитату</a>&nbsp;] [&nbsp;<a href="/search.php">Поиск</a>&nbsp;] [&nbsp;<a href="/skins.php">Шкурки</a>&nbsp;] <!--[&nbsp;<a href="/trash.php">;)</a>&nbsp;]--> [&nbsp;<a href="/rss.xml">RSS</a>&nbsp;] [&nbsp;<a href="/forum/"><b>Форум</b></a>&nbsp;]
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17703"><b>#17703</b></a></span> <span><a href="/quote.php?id=17703&amp;vote=plus" onclick="return vote(17703,'plus');">+</a> ( <span class="rate" id="q17703"> 25103 </span> ) <a href="/quote.php?id=17703&amp;vote=minus" onclick="return vote(17703,'minus');"></a></span></div>
<div class="quotbody">xxx: есть у кого жаба программер <br />xxx: кот немного пхп знает? <br />yyy: то что кот PHP немного знает, я бы ещё может поверил <br />yyy: но вот жаба-программер - это ты по-моему загнул</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17705"><b>#17705</b></a></span> <span><a href="/quote.php?id=17705&amp;vote=plus" onclick="return vote(17705,'plus');">+</a> ( <span class="rate" id="q17705"> 20507 </span> ) <a href="/quote.php?id=17705&amp;vote=minus" onclick="return vote(17705,'minus');"></a></span></div>
<div class="quotbody">ххх: 1С изначально проектировалась для небольшого количества пользователей, поэтому оператору предоставлялась бо&#039;льшая свобода действий. <br />ууу: Я склоняюсь к версии что 1С изначально вообще не проектировалась, а сразу писалась.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17707"><b>#17707</b></a></span> <span><a href="/quote.php?id=17707&amp;vote=plus" onclick="return vote(17707,'plus');">+</a> ( <span class="rate" id="q17707"> 16743 </span> ) <a href="/quote.php?id=17707&amp;vote=minus" onclick="return vote(17707,'minus');"></a></span></div>
<div class="quotbody">xxx: У меня сейчас такое странное чувство <br />xxx: Вот представь, что ты летишь на самолете. И вдруг ты узнаешь, что двигатель прикреплен к турбине резинкой от трусов <br />xxx: Вот я сейчас прочитал доки ACPI и очень похожее чувство возникает <br />yyy: Гы. А что там? <br />xxx: Там описание бинаря в BNF. И в нем циклы. <br />xxx: А еще виндовом парсере бага. А производители железа пишут ACPI для своего железа по принципу &quot;на винде работает, значит сойдет&quot; <br />xxx: Итого, 60% таблиц не соответствуют стандарту <br />yyy: Тогда это тебе только кажется, что это резинка от трусов. Это резинка от трусов только по документации и внешнему виду. На самом деле это рисунок резинки от трусов, напечатанный на туалетной бумаге</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17698"><b>#17698</b></a></span> <span><a href="/quote.php?id=17698&amp;vote=plus" onclick="return vote(17698,'plus');">+</a> ( <span class="rate" id="q17698"> 21515 </span> ) <a href="/quote.php?id=17698&amp;vote=minus" onclick="return vote(17698,'minus');"></a></span></div>
<div class="quotbody">(о DRM, защите от копирования) <br />— Диссоциативное Расстройство Меркантильности — очень частое психическое расстройство внутренней жабы, встречающееся у разработчиков игр, сопровождаемое сильными приступами паранойи. В восприятии больного его игра, будучи установленной на несколько устройств, как бы расщепляется на разные игры, за которые, по его мнению, должно быть заплачено отдельно. При этом больной становится одержим навязчивой идеей, что все его обворовывают. В большинстве случаев, это сопровождается визуальными галлюцинациями: больному мерещатся некие «пираты». Когда болезнь достигает критической стадии пациент начинает оберегать «свою прелесть» с таким усердием, что поиграть в нее становится затруднительно даже тем, кто честно за все заплатил.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17714"><b>#17714</b></a></span> <span><a href="/quote.php?id=17714&amp;vote=plus" onclick="return vote(17714,'plus');">+</a> ( <span class="rate" id="q17714"> 20101 </span> ) <a href="/quote.php?id=17714&amp;vote=minus" onclick="return vote(17714,'minus');"></a></span></div>
<div class="quotbody">Новость: в споре, что лучше - AMD или nVidia - один программист зарубил другого топором. <br />Лучший камент: Радеон Раскольников.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17715"><b>#17715</b></a></span> <span><a href="/quote.php?id=17715&amp;vote=plus" onclick="return vote(17715,'plus');">+</a> ( <span class="rate" id="q17715"> 19260 </span> ) <a href="/quote.php?id=17715&amp;vote=minus" onclick="return vote(17715,'minus');"></a></span></div>
<div class="quotbody">xxx: есть у кого жаба программер <br />xxx: кот немного пхп знает? <br />yyy: то что кот PHP немного знает, я бы ещё может поверил <br />yyy: но вот жаба-программер - это ты по-моему загнул</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17722"><b>#17722</b></a></span> <span><a href="/quote.php?id=17722&amp;vote=plus" onclick="return vote(17722,'plus');">+</a> ( <span class="rate" id="q17722"> 19419 </span> ) <a href="/quote.php?id=17722&amp;vote=minus" onclick="return vote(17722,'minus');"></a></span></div>
<div class="quotbody">xxx: из машинного перевода инструкции к бытовой технике: &quot;с 1 по 20 мая пройдут выборы ценностей в ранге&quot; (values within range 1 to 20 may be selected)</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17475"><b>#17475</b></a></span> <span><a href="/quote.php?id=17475&amp;vote=plus" onclick="return vote(17475,'plus');">+</a> ( <span class="rate" id="q17475"> 19325 </span> ) <a href="/quote.php?id=17475&amp;vote=minus" onclick="return vote(17475,'minus');"></a></span></div>
<div class="quotbody">&lt;L29Ah&gt; [[ clang++ == *g++ ]] &amp;&amp; echo yay <br />&lt;L29Ah&gt; yay <br />&lt;Minoru&gt; «*g++»? Указатели в моём шелле?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17430"><b>#17430</b></a></span> <span><a href="/quote.php?id=17430&amp;vote=plus" onclick="return vote(17430,'plus');">+</a> ( <span class="rate" id="q17430"> 19644 </span> ) <a href="/quote.php?id=17430&amp;vote=minus" onclick="return vote(17430,'minus');"></a></span></div>
<div class="quotbody">xxx: Все програмисты пое смерти в аду варятся в говнокоде :) <br />yyy: нет, в говнокоде мы варимся уже при жизни, в аду мы его рефакторим</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17419"><b>#17419</b></a></span> <span><a href="/quote.php?id=17419&amp;vote=plus" onclick="return vote(17419,'plus');">+</a> ( <span class="rate" id="q17419"> 22285 </span> ) <a href="/quote.php?id=17419&amp;vote=minus" onclick="return vote(17419,'minus');"></a></span></div>
<div class="quotbody">&quot;OpenNET: QEMU/KVM и Xen подвержены уязвимости в коде эмуляции VGA&quot; <br /> <br />xx: proxmox на форуме написали что у них падает windows в среде эмуляции после патчей, ппц. <br />yy: Правильный патч. Многоцелевой.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17414"><b>#17414</b></a></span> <span><a href="/quote.php?id=17414&amp;vote=plus" onclick="return vote(17414,'plus');">+</a> ( <span class="rate" id="q17414"> 19815 </span> ) <a href="/quote.php?id=17414&amp;vote=minus" onclick="return vote(17414,'minus');"></a></span></div>
<div class="quotbody">xxx: 600 Гб логов? У меня &gt;3 ТБ было. Дальше также место кончилось. Причина: случайно запись в лог внутри цикла написал, вместо вне него. Странно, что оно вообще работало.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17396"><b>#17396</b></a></span> <span><a href="/quote.php?id=17396&amp;vote=plus" onclick="return vote(17396,'plus');">+</a> ( <span class="rate" id="q17396"> 19330 </span> ) <a href="/quote.php?id=17396&amp;vote=minus" onclick="return vote(17396,'minus');"></a></span></div>
<div class="quotbody">в офисе Apple: <br />- Может сделаем новый дизайн? <br />- Та не, мы меняли его уже пару лет назад, у кого еще идеи? <br />- А давайте исправим глюки на старых девайсах? <br />- Зачем? пусть покупают новые! <br />- Давайте добавим новые Emoji? <br />- Гениально! Так и сделаем!</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17394"><b>#17394</b></a></span> <span><a href="/quote.php?id=17394&amp;vote=plus" onclick="return vote(17394,'plus');">+</a> ( <span class="rate" id="q17394"> 18914 </span> ) <a href="/quote.php?id=17394&amp;vote=minus" onclick="return vote(17394,'minus');"></a></span></div>
<div class="quotbody">xxx: Саша, речь не о том, что теоретически возможно прочесть произведения классики, и даже есть те, кто прочитывает. Мануал администратора фриБЗДи Ты тоже, наверно, одолел, и возможно, что на одном дыхании - Ты станешь на этом основании утверждать, что рекомый мануал есть выдающийся памятник словесности, написан легко, увлекательно и очень душевно? <br /> <br />yyy: Мануал администратора фрибсд, сравнительно с оракловой документацией, это выдающийся, мать его так, памятник словесности. Написан легко, увлекательно, и очень душевно.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17386"><b>#17386</b></a></span> <span><a href="/quote.php?id=17386&amp;vote=plus" onclick="return vote(17386,'plus');">+</a> ( <span class="rate" id="q17386"> 18888 </span> ) <a href="/quote.php?id=17386&amp;vote=minus" onclick="return vote(17386,'minus');"></a></span></div>
<div class="quotbody">&gt; Компания Mail.Ru &lt;...&gt; будет предоставлять услуги коммерческой поддержки решений на базе свободной СУБД Tarantool <br /> <br />SELECT * FROM cars; <br /> <br />+------+--------------------+ <br />| id | name | <br />+------+--------------------+ <br />| 1 | &quot;Mazda CX-3&quot; | <br />| 2 | &quot;Audi Q1&quot; | <br />| 3 | &quot;BMW X1&quot; | <br />| 4 | &quot;Mazda CX-5&quot; | <br />| 5 | &quot;Cadillac XT5&quot; | <br />| NULL | &quot;Спутник@Mail.Ru&quot; | <br />| NULL | &quot;Guard@Mail.ru&quot; | <br />| NULL | &quot;Агент@Mail.ru &quot; | <br />| NULL | &quot;Mail.ru Updater&quot; | <br />+------+--------------------+</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17381"><b>#17381</b></a></span> <span><a href="/quote.php?id=17381&amp;vote=plus" onclick="return vote(17381,'plus');">+</a> ( <span class="rate" id="q17381"> 19273 </span> ) <a href="/quote.php?id=17381&amp;vote=minus" onclick="return vote(17381,'minus');"></a></span></div>
<div class="quotbody">Дело в том, что любые новые фичи C++ должны пройти через комитет трёх фанатиков. <br /> <br />Первый фанатик обожает исключения. Всё собрание этот ворчливый старик кажется спящим, но в самый неудобный момент вскакаивает и перебивает говорящего криком «и тут мы бросаем исключение!» После этого конечно всё ломается. Этот старик мало кому нравится, но все вынуждены его терпеть. <br /> <br />Второй фанатик обожает шаблоны. Любую фичу он нежно оборачивает в шаблоны, которые заворачивает в шаблоны, которые заворачивает в шаблоны… пока она опять не сломается. В свободное время пытается написать программу по вычислению смысла жизни и вообще на этапе компиляции. <br /> <br />Третий фанатик, самый молодой, обожает всё параллельное. В отличие от других, он не критикует сразу. Он с энтузиазмом хватается за предложенную фичу, сразу переводит его в параллельность, убеждается что всё ломается и со вздохом «в наш век параллельного программирования так делать нельзя» отправляет фичу в корзину. Говорят, у него множество личностей, которые друг перебивают часто друга.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17375"><b>#17375</b></a></span> <span><a href="/quote.php?id=17375&amp;vote=plus" onclick="return vote(17375,'plus');">+</a> ( <span class="rate" id="q17375"> 34347 </span> ) <a href="/quote.php?id=17375&amp;vote=minus" onclick="return vote(17375,'minus');"></a></span></div>
<div class="quotbody">клиент: Непингуется хост serv29. Посмотрите что с ним. <br />админ: Посмотрел на него, пинг появился. <br />клиент: А что с ним было? <br />админ: хз. я только посмотел. Могу объяснить это приниципом неопределенности: наблюдение влияет на результат.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17371"><b>#17371</b></a></span> <span><a href="/quote.php?id=17371&amp;vote=plus" onclick="return vote(17371,'plus');">+</a> ( <span class="rate" id="q17371"> 626 </span> ) <a href="/quote.php?id=17371&amp;vote=minus" onclick="return vote(17371,'minus');"></a></span></div>
<div class="quotbody">это гениально - вырядиться в костюм пингвина и ходить по улице, приставая к людям с вопросом: &quot;не хотите ли вы поговорить о линуксе?&quot; После чего вручать брошюрки про gentoo... хД</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17370"><b>#17370</b></a></span> <span><a href="/quote.php?id=17370&amp;vote=plus" onclick="return vote(17370,'plus');">+</a> ( <span class="rate" id="q17370"> 677 </span> ) <a href="/quote.php?id=17370&amp;vote=minus" onclick="return vote(17370,'minus');"></a></span></div>
<div class="quotbody">Обсуждение российского моноблока &quot;Таволга&quot;: <br />zzz: &quot;Как нам пояснили в компании &quot;???&quot;, &quot;не хотелось придумывать очередное безликое латинизированное название, обычно ассоциирующееся с IT, или аббревиатуру. Нам хотелось, чтобы название было узнаваемо русским, а не псевдо-западным, мелодичным и при этом не банальным вот так из множества вариантов выбрали Таволгу&quot;.&quot; <br /> <br />скоро так начнут и кодить на кирили...ах, ну да <br /> <br />xxx: Процессор Intel Core i5-5287U <br /> <br />yyy: Таволга, или Лаба&amp;#769;зник (лат. Filip&amp;#233;ndula) — род многолетних трав семейства Розовые (Rosaceae). Насчитывает 10—13 видов[3], произрастающих в умеренной зоне Северного полушария. <br />Садовое применение: <br />Великолепно отпугивает мух, комаров, слепней. <br /> <br />Походу попали в точку <br /> <br />zzz: Так ребята там здоровенный фумигатор на пятом коре запилили</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17369"><b>#17369</b></a></span> <span><a href="/quote.php?id=17369&amp;vote=plus" onclick="return vote(17369,'plus');">+</a> ( <span class="rate" id="q17369"> 1293 </span> ) <a href="/quote.php?id=17369&amp;vote=minus" onclick="return vote(17369,'minus');"></a></span></div>
<div class="quotbody">Grother: все знают много историй про то, как шампунь или ещё какая хрень в ванной путалась с её кремом для депиляции. Но мало кому известны истории о том, как она мазала прыщи из симпатичного маленького тюбика с непонятным названием Pasta silikonova termoprzewodzaca.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17367"><b>#17367</b></a></span> <span><a href="/quote.php?id=17367&amp;vote=plus" onclick="return vote(17367,'plus');">+</a> ( <span class="rate" id="q17367"> 21 </span> ) <a href="/quote.php?id=17367&amp;vote=minus" onclick="return vote(17367,'minus');"></a></span></div>
<div class="quotbody">xxx: Свеже-родившийся анекдот - сколько нужно айтишников чтобы переткнуть сервер в другую подсеть? <br />xxx: Ответ: пять. Два тестировщика, два инжнера и админ. <br />xxx: Тестировщикам нужно но они не знают, инженеры знают но им нельзя. А админ пустил всех в серверную.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17363"><b>#17363</b></a></span> <span><a href="/quote.php?id=17363&amp;vote=plus" onclick="return vote(17363,'plus');">+</a> ( <span class="rate" id="q17363"> -17 </span> ) <a href="/quote.php?id=17363&amp;vote=minus" onclick="return vote(17363,'minus');"></a></span></div>
<div class="quotbody">Программисту по багам программы: <br />1.При выборе даты постоянно вылетает необрабатываемое исключение. <br />2.Если нажать на кнопку &quot;фильтр&quot; пока идёт создание фильтра - возникает необрабатываемое исключение. <br />Ответ: <br />П.1 Это ошибка не программы а её окружения, т.е. пофикси винду. <br />П.2 куда ты торопишься?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17359"><b>#17359</b></a></span> <span><a href="/quote.php?id=17359&amp;vote=plus" onclick="return vote(17359,'plus');">+</a> ( <span class="rate" id="q17359"> 41 </span> ) <a href="/quote.php?id=17359&amp;vote=minus" onclick="return vote(17359,'minus');"></a></span></div>
<div class="quotbody">Обсуждение ReactOS 0.4 на ЛОРе: <br /> <br />Oxdeadbeef: Оно может в x86_64? <br /> <br />Jedi-to-be: Может, но пока нет.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17358"><b>#17358</b></a></span> <span><a href="/quote.php?id=17358&amp;vote=plus" onclick="return vote(17358,'plus');">+</a> ( <span class="rate" id="q17358"> 9 </span> ) <a href="/quote.php?id=17358&amp;vote=minus" onclick="return vote(17358,'minus');"></a></span></div>
<div class="quotbody">&lt;dsmirnov&gt; на супермикро и в отвратительных шкафах ..... а вы лабутены, лабутены ....</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17357"><b>#17357</b></a></span> <span><a href="/quote.php?id=17357&amp;vote=plus" onclick="return vote(17357,'plus');">+</a> ( <span class="rate" id="q17357"> 37 </span> ) <a href="/quote.php?id=17357&amp;vote=minus" onclick="return vote(17357,'minus');"></a></span></div>
<div class="quotbody">разраб: деплоим. 20 пендингов <br />тестер: боже, сохрани <br />разраб: не поможет, место на облаке закончилось</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17356"><b>#17356</b></a></span> <span><a href="/quote.php?id=17356&amp;vote=plus" onclick="return vote(17356,'plus');">+</a> ( <span class="rate" id="q17356"> 17 </span> ) <a href="/quote.php?id=17356&amp;vote=minus" onclick="return vote(17356,'minus');"></a></span></div>
<div class="quotbody">xxx: Программно-аппаратная платформа удаленного администрирования автоматизированных систем. Сокращенно - ПАПУАС</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17354"><b>#17354</b></a></span> <span><a href="/quote.php?id=17354&amp;vote=plus" onclick="return vote(17354,'plus');">+</a> ( <span class="rate" id="q17354"> 13 </span> ) <a href="/quote.php?id=17354&amp;vote=minus" onclick="return vote(17354,'minus');"></a></span></div>
<div class="quotbody">Nick&gt; Что вы вообще понимаете в тормозных серверах <br />Nick&gt; Мой пишет “System information disabled due to load higher than 1.0” <br />Nick&gt; Вот только при этом вилка локалки рядом на столе лежит, вынутая из сетевухи <br />Nick&gt; Для селерона 400, в который через PCI-адаптер SATA воткнут терабайтник… <br />Nick&gt; да для него просто дышать — и то уже физкультура</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17353"><b>#17353</b></a></span> <span><a href="/quote.php?id=17353&amp;vote=plus" onclick="return vote(17353,'plus');">+</a> ( <span class="rate" id="q17353"> 6 </span> ) <a href="/quote.php?id=17353&amp;vote=minus" onclick="return vote(17353,'minus');"></a></span></div>
<div class="quotbody">Мой мир никогда не станет прежним. Сегодня я узнал, что RoHS это не название китайской фирмы по выпуску электронных компонентов, а директива по содержанию в них вредных веществ</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17316"><b>#17316</b></a></span> <span><a href="/quote.php?id=17316&amp;vote=plus" onclick="return vote(17316,'plus');">+</a> ( <span class="rate" id="q17316"> 33 </span> ) <a href="/quote.php?id=17316&amp;vote=minus" onclick="return vote(17316,'minus');"></a></span></div>
<div class="quotbody">Елена: Дали строителям проект, они строили-строили и наконец построили. Приезжает заказчик. В грунте выкопана цилиндрическая яма метров 15 в глубину. На дне сияет прожектор. Заказчик переворачивает чертёж на 180 градусов и говорит: “Всё хорошо, но по проекту здесь должен был быть МАЯК”. <br />Dmitry: Боженьки мои ))))) <br />Елена: немножко похоже на историю из жизни программистов <br />Dmitry: ага ) только программист может потом решить, что будет легче все корабли сделать подземными, чем переделывать маяк.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17314"><b>#17314</b></a></span> <span><a href="/quote.php?id=17314&amp;vote=plus" onclick="return vote(17314,'plus');">+</a> ( <span class="rate" id="q17314"> 3 </span> ) <a href="/quote.php?id=17314&amp;vote=minus" onclick="return vote(17314,'minus');"></a></span></div>
<div class="quotbody">Kikimorra: Изучаю джаваскрипт на онлайн-курсах. Чувак у доски рассказывает, как удалять ноды. Приводимая в пример веб-страница выглядит так: <br /> <br />It&#039;s a nice day! <br />и кнопка <br />Delete all children! <br /> <br />Прям кодишь и чувствуешь, как рога с хвостом прорастают &gt;.&lt;</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17312"><b>#17312</b></a></span> <span><a href="/quote.php?id=17312&amp;vote=plus" onclick="return vote(17312,'plus');">+</a> ( <span class="rate" id="q17312"> 15 </span> ) <a href="/quote.php?id=17312&amp;vote=minus" onclick="return vote(17312,'minus');"></a></span></div>
<div class="quotbody">xxx:после апдейта винда взяла и переставила панель задач слева обратно вниз <br />xxx:мол, не выёбывайся <br />ххх:тебе не убунта</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17306"><b>#17306</b></a></span> <span><a href="/quote.php?id=17306&amp;vote=plus" onclick="return vote(17306,'plus');">+</a> ( <span class="rate" id="q17306"> 25 </span> ) <a href="/quote.php?id=17306&amp;vote=minus" onclick="return vote(17306,'minus');"></a></span></div>
<div class="quotbody">aaa: Есть же специальные для тачскринов, там кончики пальцев сделаны из проводящего материала. <br />bbb: Оо, как они называются? А то я видел только вязаные igloves, которые рвутся через месяц использования, И в мороз в них не походишь. <br />ccc: Купите металлизированные нитки и сделайте несколько стежков под подушечками пальцев. Начинайте прошивать изнутри оставив конец нитки после узелка подлиннее, чтобы обеспечить лучшую проводимость. <br />ddd: — Как ты работаешь с айпадом в перчатках? <br />— Я их перепрошил.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17304"><b>#17304</b></a></span> <span><a href="/quote.php?id=17304&amp;vote=plus" onclick="return vote(17304,'plus');">+</a> ( <span class="rate" id="q17304"> -1 </span> ) <a href="/quote.php?id=17304&amp;vote=minus" onclick="return vote(17304,'minus');"></a></span></div>
<div class="quotbody">&lt;&gt; нет ничего приятнее теплого лампового диалапа... <br />&lt;&gt; когда жужжание фрезы сливается с звуками хэндшейка в гармоничную мелодию?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17297"><b>#17297</b></a></span> <span><a href="/quote.php?id=17297&amp;vote=plus" onclick="return vote(17297,'plus');">+</a> ( <span class="rate" id="q17297"> -11 </span> ) <a href="/quote.php?id=17297&amp;vote=minus" onclick="return vote(17297,'minus');"></a></span></div>
<div class="quotbody">[15:15:56] r@ttler: говно успешно прилеплено и оттестировано. говно оказалось говном <br />[15:16:05] ZimM: внезапно <br />[15:17:30] r@ttler: ну я пока его прилеплял мне аж привидилась картина: замок разраба говна. дорога к нему увенчана костылями. как вот копья вешали с черепами врагов, так тут костыли с черепами разрабов <br />[15:17:59] r@ttler: и табличка на воротах &quot;оставь свой мозг, всяк сюда входящий&quot;</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17291"><b>#17291</b></a></span> <span><a href="/quote.php?id=17291&amp;vote=plus" onclick="return vote(17291,'plus');">+</a> ( <span class="rate" id="q17291"> 8 </span> ) <a href="/quote.php?id=17291&amp;vote=minus" onclick="return vote(17291,'minus');"></a></span></div>
<div class="quotbody">Если бы врачи были ИТ-шниками: <br /> <br />- У меня не работает клавиатура. <br /> <br />- Сдайте пробы на подклавиатурный сахар и лактозу, сделайте рентген и функциональный тест клавиш.... <br />[через 2 месяца и 20 тысяч рублей]...Действительно, обнаружена карамельная бляшка под пробелом. <br /> <br />- Что же делать? <br /> <br />- Вам показана консервативная терапия: ежедневно постукивайте 15 минут по перевернутой клавиатуре, затем 15 минут протирайте клавишу спиртом, потом разрабатывайте клавишу вручную. До конца срока службы вашего системного блока старайтесь как можно реже и аккуратнее пользоваться пробелом. Поставьте виртуальную клавиатуру и печатайте мышкой.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17285"><b>#17285</b></a></span> <span><a href="/quote.php?id=17285&amp;vote=plus" onclick="return vote(17285,'plus');">+</a> ( <span class="rate" id="q17285"> 0 </span> ) <a href="/quote.php?id=17285&amp;vote=minus" onclick="return vote(17285,'minus');"></a></span></div>
<div class="quotbody">ZimM: нуу... with great power comes great responsibility <br />r@ttler: great chances to shoot your own leg <br />ZimM: ну это да. другое дело, что для этого все равно нужно постараться <br />r@ttler: work hard to shoot your own leg? <br />r@ttler: ну тогда ты совсем лол <br />r@ttler: впадло было делать по-нормальному, потому помучался и таки сделал через жопу <br />ZimM: ну, я думал, что отстрелю себе фалангу мизинца, а оторвал пол-туловища, потому что пол-туловища мне показались похожими на фалангу мизинца...</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17280"><b>#17280</b></a></span> <span><a href="/quote.php?id=17280&amp;vote=plus" onclick="return vote(17280,'plus');">+</a> ( <span class="rate" id="q17280"> 11 </span> ) <a href="/quote.php?id=17280&amp;vote=minus" onclick="return vote(17280,'minus');"></a></span></div>
<div class="quotbody">xxx: Вот говорят иногда &quot;зоопарк браузеров&quot; (операционных систем, железок и так далее), а я тут внезапно понял, что у меня самый настоящий бордель виртуальных машин. Потому что их у меня четыре, названы женскими именами, чтобы быстро различать, и я с ними трахаюсь. Причём в данный момент со всеми четырьмя одновременно, потому что делаю лабораторную по сетям.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17279"><b>#17279</b></a></span> <span><a href="/quote.php?id=17279&amp;vote=plus" onclick="return vote(17279,'plus');">+</a> ( <span class="rate" id="q17279"> 2 </span> ) <a href="/quote.php?id=17279&amp;vote=minus" onclick="return vote(17279,'minus');"></a></span></div>
<div class="quotbody">Val: США провели третье и последнее испытание новой атомной бомбы B61-12. Бомбу без заряда, в соответствии с международным договором о запрете ядерных взрывов, сбросили с истребителя F-15E на полигоне &quot;Тонопа&quot; в Неваде 20 октября. <br />Val: какое интересное испытание. При ударе о землю она выбросила флажок &quot;БУМ&quot;? <br />Кир: Отправила в твиттер &quot;БДЫЩ!&quot; - она жы высокотехнологичная</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17276"><b>#17276</b></a></span> <span><a href="/quote.php?id=17276&amp;vote=plus" onclick="return vote(17276,'plus');">+</a> ( <span class="rate" id="q17276"> 25 </span> ) <a href="/quote.php?id=17276&amp;vote=minus" onclick="return vote(17276,'minus');"></a></span></div>
<div class="quotbody">dimgel: (ссылка на ленту.ру) &quot;Linux.Encoder.1 — относится к классу троянцев-шифровальщиков. После запуска с правами администратора...&quot; <br />Бгг. Ещё сто лет назад шутка ходила про линуксовые вирусы: &quot;распакуйте меня пожалуйста и пропишите в автозапуск демоном от имени рута&quot;. <br />dimgel: Я смотрю, ни хера не меняется в этой жизни. <br />garik: на ещё надо пару патчей найти и накатить <br />garik: иначе не скомпилится <br />dimgel: обязательно <br />dimgel: причём для разных дистров патчи будут разные <br />dimgel: Народ! Как пропатчить Linux.Encoder.1 под FreeBSD?!</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17272"><b>#17272</b></a></span> <span><a href="/quote.php?id=17272&amp;vote=plus" onclick="return vote(17272,'plus');">+</a> ( <span class="rate" id="q17272"> -5 </span> ) <a href="/quote.php?id=17272&amp;vote=minus" onclick="return vote(17272,'minus');"></a></span></div>
<div class="quotbody">xxx: когда читаю такие ограничения дурацкие, хочется спросить, каким местом пишутся драйвера <br />yyy: Но тут хоть плюс, что он сам не падает, а ошибку только выдаёт. <br />xxx: андроид разработчик... <br />xxx: ну хорошо хоть не падает вместе с системой!! <br />xxx: а то что всё в говне и не работает это мелочи <br />xxx: дальше будет &quot;ну хоть не сносит систему&quot; <br />xxx: &quot;ну хоть не выжигает гпу&quot; <br />yyy: &quot;ну, хоть не выжигает глаза&quot; <br />xxx: &quot;ну хоть не подключается к скайнету и не выжигает поверхность планеты&quot; <br />yyy: &quot;ну, хотя бы не уничтожает вселенную&quot; <br />xxx: вот видишь, повезло-то как!</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17259"><b>#17259</b></a></span> <span><a href="/quote.php?id=17259&amp;vote=plus" onclick="return vote(17259,'plus');">+</a> ( <span class="rate" id="q17259"> 17 </span> ) <a href="/quote.php?id=17259&amp;vote=minus" onclick="return vote(17259,'minus');"></a></span></div>
<div class="quotbody">обсуждение странного результата трейсроута <br />[19:27:36] ZimM: пробил по геоип. реально Europe, но если по координатам глянуть - то швейцария <br />[19:28:01] r@ttler: и пробей соседний. реально сша? <br />[19:28:30] ZimM: реально сша <br />[19:28:49] r@ttler: ну значит лол <br />[19:29:01] r@ttler: сто раз туда-сюда по трансатлантике? <br />[19:29:08] ZimM: ну а хуле <br />[19:29:14] r@ttler: вокруг света за 80 хопов?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17236"><b>#17236</b></a></span> <span><a href="/quote.php?id=17236&amp;vote=plus" onclick="return vote(17236,'plus');">+</a> ( <span class="rate" id="q17236"> 13 </span> ) <a href="/quote.php?id=17236&amp;vote=minus" onclick="return vote(17236,'minus');"></a></span></div>
<div class="quotbody">xxx: &quot;Как перестать юзать чужой код и научиться прогать самостоятельно&quot;, новый бестселлер Алан Карра</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17235"><b>#17235</b></a></span> <span><a href="/quote.php?id=17235&amp;vote=plus" onclick="return vote(17235,'plus');">+</a> ( <span class="rate" id="q17235"> 12 </span> ) <a href="/quote.php?id=17235&amp;vote=minus" onclick="return vote(17235,'minus');"></a></span></div>
<div class="quotbody">xx: Флеш умер, google его выпилит скоро :) <br /> <br />yy: Джобс тоже так говорил) <br /> <br />zz: Джобса уже бог (или ктулху, или матрица - кому что нравится) выпилил, а флэш еще барахтается <br /> <br />tt: Ну разве не ясно, что это флеш его и выпилил?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17233"><b>#17233</b></a></span> <span><a href="/quote.php?id=17233&amp;vote=plus" onclick="return vote(17233,'plus');">+</a> ( <span class="rate" id="q17233"> 4 </span> ) <a href="/quote.php?id=17233&amp;vote=minus" onclick="return vote(17233,'minus');"></a></span></div>
<div class="quotbody">К новости &quot;Отечественный защищённый Linux-дистрибутив Заря готов к внедрению&quot;: <br /> <br />xxx: Теперь будет сборка-разборка не только автомата, но и ядра. Норматив 3 минуты.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17230"><b>#17230</b></a></span> <span><a href="/quote.php?id=17230&amp;vote=plus" onclick="return vote(17230,'plus');">+</a> ( <span class="rate" id="q17230"> 24 </span> ) <a href="/quote.php?id=17230&amp;vote=minus" onclick="return vote(17230,'minus');"></a></span></div>
<div class="quotbody">С Хабра: <br />Смотрели Last Exile, где были шахматы для воздушных кораблей — с фиксацией? Вот термос для поезда. У него горлышко как у чайника, но оно перекрыто. Кнопка на ручке открывает возможность лить из термоса наружу. Это классика страховки от ошибок. Пользоваться потенциально опасной функцией можно только сознательно. <br />[...] <br />Это ещё и защита от дурака, в частности, важная для техники безопасности. У нас на производстве есть станок, который умеет прошибать гильотинным ножиком сразу огромную пачку бумаги. Так вот, чтобы его запустить нужно: <br /> <br />1) Положить бумагу под датчик бумаги <br />2) Положить левую руку на левую пусковую кнопку далеко слева <br />3) Правую руку — на правую пусковую кнопку далеко справа <br />4) Нажать педаль ногой (в этом положении физически невозможно засунуть голову в рабочую область станка) <br />5) Нажать обе пусковые кнопки одновременно <br />6) Но, видимо, рабочие научились действовать вдвоём или блокировать кнопки — и поэтому ещё нужно убрать всё из рабочей зоны, чтобы инфракрасные лучи не пересекались. Только после этого случится пуск.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17226"><b>#17226</b></a></span> <span><a href="/quote.php?id=17226&amp;vote=plus" onclick="return vote(17226,'plus');">+</a> ( <span class="rate" id="q17226"> 15 </span> ) <a href="/quote.php?id=17226&amp;vote=minus" onclick="return vote(17226,'minus');"></a></span></div>
<div class="quotbody">Архитекторы, емае, это источник нескончаемого умиления. Планерка, разбор какого-то легаси модуля. Говорят переписывать будем. Архитектор его анализировал неделю. Читал там код, компилил что-то, виртуалок поднял кучу, думал спеку, ходил курить каждые 15 и посадил картридж в принтере. Вот выходит этот мегачеловече к вайтборду докладывать комманде об устройстве этой вундервафли. Берет маркер, долго думает, рисует кружок. Еще думает, закрашивает. Думает еще, рисует входящую стрелку. Потом еще исходящую. Поворачивается к комманде: <br /> <br />-- В общем, коллеги, это жопа. Работает все через нее. А теперь посмотрим дему и обсудим баги. <br /> <br />Я вообще удивлен что в они в свой UML до сих пор стандартный символ не добавили.</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17227"><b>#17227</b></a></span> <span><a href="/quote.php?id=17227&amp;vote=plus" onclick="return vote(17227,'plus');">+</a> ( <span class="rate" id="q17227"> 7 </span> ) <a href="/quote.php?id=17227&amp;vote=minus" onclick="return vote(17227,'minus');"></a></span></div>
<div class="quotbody">xxx: О, Боже! Я начинаю получать кайф от пользования линуксом... Развернул убунту, понадобилось пару программ поставить, я ему пишу в консоли аптгет инсталл со списком желаемого, а он сам ищет, ставит и настраивает, и не надо дистрибутивы искать, по сайтам лазить, качать...</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17223"><b>#17223</b></a></span> <span><a href="/quote.php?id=17223&amp;vote=plus" onclick="return vote(17223,'plus');">+</a> ( <span class="rate" id="q17223"> 12 </span> ) <a href="/quote.php?id=17223&amp;vote=minus" onclick="return vote(17223,'minus');"></a></span></div>
<div class="quotbody">xxx: 40 Ватт, бывали у АМД и помощнее, если вы понимаете... <br />yyy: у меня был пентиум D <br />yyy: этим все сказано <br />yyy: когда я запускал эклипс - зимой наступало лето <br />zzz: у меня тоже греет комнату хорошо :) <br />zzz: сейчас 80 градусов <br />xxx: да это не комната - это баня ))) <br />yyy: живучий однако <br />yyy: мой спалил три мамки <br />yyy: у всех диагноз - микротрещины <br />yyy: это была середина 2000х, обычный офисный корпус, потолок регистрировал примерно 120 градусов <br />yyy: я летом морозил лед и крошил его в миску перед воздухозаборным отверстием <br />yyy: и менял его раз в час <br />yyy: когда грузилась моя винда, запотевали окна соседних домов <br />yyy: однажды, когда я архивировал сериал на флешку, ученые отметили глобальное потепление на 5 градусов <br />yyy: каждый раз, как я компилировал проект, в антарктиде семейство пингвинов оставалось без гнезда <br />xxx: мне кажется, что Чак тебе начинает завидовать <br />yyy: чак приходил ко мне домой разгоревать свой завтрак <br />yyy: свой процессор я отдал ученым, и в итоге он лег в основу ИТЭР</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17224"><b>#17224</b></a></span> <span><a href="/quote.php?id=17224&amp;vote=plus" onclick="return vote(17224,'plus');">+</a> ( <span class="rate" id="q17224"> 23 </span> ) <a href="/quote.php?id=17224&amp;vote=minus" onclick="return vote(17224,'minus');"></a></span></div>
<div class="quotbody">— Человек, который работает в ИБ, не знает, что такое NDA? <br />— Может быть ему по NDA нельзя говорить, что знает?</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17221"><b>#17221</b></a></span> <span><a href="/quote.php?id=17221&amp;vote=plus" onclick="return vote(17221,'plus');">+</a> ( <span class="rate" id="q17221"> 32 </span> ) <a href="/quote.php?id=17221&amp;vote=minus" onclick="return vote(17221,'minus');"></a></span></div>
<div class="quotbody">duzorg: <br />У кондиционера заклинило дренаж и вся вода вылилась внутрь серверной. Благо я туда случайно зашёл. Там серваки стояли на деревянной столешнице, а её водой размыло, она посередине где то на 15-20см прогнулась. Еще бы немного и рухнули бы все серваки на пол. Тысяч на 100 попали бы... <br />Или скорее даже больше чем на 100. <br />duzorg: <br />А в серверную я зашёл, потому что микротик завис. А микротик впервые за 4 года завис. %) <br />Funkryer: <br />вот так повезло <br />отличный был бы отзыв о микротике =) <br />мол завис 1 раз за 4 года и то только для того, чтобы спасти наши серваки <br />duzorg: <br />Не только работает стабильно, но и заботится о стабильной работе рядом находящегося оборудования ))) <br />duzorg: <br />На самом деле может его от большой влажности чё нить клинануло... хз... <br />Funkryer: <br />ну, начал, не порть магию!</div>
</div><hr />
<div class="quote">
<div class="quothead"><span><a href="/quote.php?id=17220"><b>#17220</b></a></span> <span><a href="/quote.php?id=17220&amp;vote=plus" onclick="return vote(17220,'plus');">+</a> ( <span class="rate" id="q17220"> 12 </span> ) <a href="/quote.php?id=17220&amp;vote=minus" onclick="return vote(17220,'minus');"></a></span></div>
<div class="quotbody">xxx: конструктор запросов как всегда гентален <br />xxx: :( <br />xxx: хм хотел написать гениален, но генитален тоже пойдет</div>
</div><hr />
<div class="pages">
Страницы: 1 <a href="/?page=2">2</a> <a href="/?page=3">3</a> <a href="/?page=4">4</a> <a href="/?page=5">5</a> <a href="/?page=6">6</a> <a href="/?page=7">7</a> <a href="/?page=8">8</a> <a href="/?page=9">9</a> <a href="/?page=10">10</a> <a href="/?page=11">11</a> <a href="/?page=12">12</a> <a href="/?page=13">13</a> <a href="/?page=14">14</a> <a href="/?page=15">15</a> <a href="/?page=16">16</a> <a href="/?page=17">17</a> <a href="/?page=18">18</a> <a href="/?page=19">19</a> <a href="/?page=20">20</a> <a href="/?page=21">21</a> <a href="/?page=22">22</a> <a href="/?page=23">23</a> <a href="/?page=24">24</a> <a href="/?page=25">25</a> <a href="/?page=26">26</a> <a href="/?page=27">27</a> <a href="/?page=28">28</a> <a href="/?page=29">29</a> <a href="/?page=30">30</a> <a href="/?page=31">31</a> <a href="/?page=32">32</a> <a href="/?page=33">33</a> <a href="/?page=34">34</a> <a href="/?page=35">35</a> <a href="/?page=36">36</a> <a href="/?page=37">37</a> <a href="/?page=38">38</a> <a href="/?page=39">39</a> <a href="/?page=40">40</a> <a href="/?page=41">41</a> <a href="/?page=42">42</a> <a href="/?page=43">43</a> <a href="/?page=44">44</a> <a href="/?page=45">45</a> <a href="/?page=46">46</a> <a href="/?page=47">47</a> <a href="/?page=48">48</a> <a href="/?page=49">49</a> <a href="/?page=50">50</a> <a href="/?page=51">51</a> <a href="/?page=52">52</a> <a href="/?page=53">53</a> <a href="/?page=54">54</a> <a href="/?page=55">55</a> </div><hr />
<div class="menu">
[&nbsp;По дате&nbsp;] [&nbsp;<a href="/best.php">По рейтингу</a>&nbsp;] [&nbsp;<a href="/random.php">Случайно</a>&nbsp;] [&nbsp;<a href="/add.php">Добавить цитату</a>&nbsp;] [&nbsp;<a href="/search.php">Поиск</a>&nbsp;] [&nbsp;<a href="/skins.php">Шкурки</a>&nbsp;] <!--[&nbsp;<a href="/trash.php">;)</a>&nbsp;]--> [&nbsp;<a href="/rss.xml">RSS</a>&nbsp;] [&nbsp;<a href="/forum/"><b>Форум</b></a>&nbsp;]
</div><hr />
<div class="copy">
«ibash.org.ru — Новый цитатник Рунета»<br />
Почта вебмастера: <a href="&#109;&#097;&#105;&#108;&#116;&#111;&#058;%20%69&#109;&#097;&#105;%6c%40%69&#098;%61&#115;%68&#046;%6f&#114;%67&#046;&#114;&#117;">&#105;&#109;&#097;&#105;&#108;@&#105;&#098;&#097;&#115;&#104;&#046;&#111;&#114;&#103;&#046;&#114;&#117;</a>
</div>
<div class="topline"> </div>
</body>
</html>

View File

@ -1,359 +0,0 @@
<?xml version="1.0" encoding="windows-1251"?>
<rss version="2.0">
<channel>
<title>iBash.Org.Ru</title>
<link>http://ibash.org.ru/</link>
<description>Новый цитатник Рунета</description>
<language>ru</language>
<item>
<guid>http://ibash.org.ru/quote.php?id=17703</guid>
<link>http://ibash.org.ru/quote.php?id=17703</link>
<title>Цитата #17703</title>
<pubDate>Wed, 21 Mar 2018 10:27:32 +0300</pubDate>
<description><![CDATA[xxx: есть у кого жаба программер <br />xxx: кот немного пхп знает? <br />yyy: то что кот PHP немного знает, я бы ещё может поверил <br />yyy: но вот жаба-программер - это ты по-моему загнул]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17705</guid>
<link>http://ibash.org.ru/quote.php?id=17705</link>
<title>Цитата #17705</title>
<pubDate>Wed, 21 Mar 2018 10:27:22 +0300</pubDate>
<description><![CDATA[ххх: 1С изначально проектировалась для небольшого количества пользователей, поэтому оператору предоставлялась бо&#039;льшая свобода действий. <br />ууу: Я склоняюсь к версии что 1С изначально вообще не проектировалась, а сразу писалась.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17707</guid>
<link>http://ibash.org.ru/quote.php?id=17707</link>
<title>Цитата #17707</title>
<pubDate>Wed, 21 Mar 2018 10:26:48 +0300</pubDate>
<description><![CDATA[xxx: У меня сейчас такое странное чувство <br />xxx: Вот представь, что ты летишь на самолете. И вдруг ты узнаешь, что двигатель прикреплен к турбине резинкой от трусов <br />xxx: Вот я сейчас прочитал доки ACPI и очень похожее чувство возникает <br />yyy: Гы. А что там? <br />xxx: Там описание бинаря в BNF. И в нем циклы. <br />xxx: А еще виндовом парсере бага. А производители железа пишут ACPI для своего железа по принципу &quot;на винде работает, значит сойдет&quot; <br />xxx: Итого, 60% таблиц не соответствуют стандарту <br />yyy: Тогда это тебе только кажется, что это резинка от трусов. Это резинка от трусов только по документации и внешнему виду. На самом деле это рисунок резинки от трусов, напечатанный на туалетной бумаге]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17698</guid>
<link>http://ibash.org.ru/quote.php?id=17698</link>
<title>Цитата #17698</title>
<pubDate>Thu, 15 Mar 2018 10:15:42 +0300</pubDate>
<description><![CDATA[(о DRM, защите от копирования) <br />— Диссоциативное Расстройство Меркантильности — очень частое психическое расстройство внутренней жабы, встречающееся у разработчиков игр, сопровождаемое сильными приступами паранойи. В восприятии больного его игра, будучи установленной на несколько устройств, как бы расщепляется на разные игры, за которые, по его мнению, должно быть заплачено отдельно. При этом больной становится одержим навязчивой идеей, что все его обворовывают. В большинстве случаев, это сопровождается визуальными галлюцинациями: больному мерещатся некие «пираты». Когда болезнь достигает критической стадии пациент начинает оберегать «свою прелесть» с таким усердием, что поиграть в нее становится затруднительно даже тем, кто честно за все заплатил.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17714</guid>
<link>http://ibash.org.ru/quote.php?id=17714</link>
<title>Цитата #17714</title>
<pubDate>Thu, 15 Mar 2018 10:14:00 +0300</pubDate>
<description><![CDATA[Новость: в споре, что лучше - AMD или nVidia - один программист зарубил другого топором. <br />Лучший камент: Радеон Раскольников.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17715</guid>
<link>http://ibash.org.ru/quote.php?id=17715</link>
<title>Цитата #17715</title>
<pubDate>Thu, 15 Mar 2018 10:13:35 +0300</pubDate>
<description><![CDATA[xxx: есть у кого жаба программер <br />xxx: кот немного пхп знает? <br />yyy: то что кот PHP немного знает, я бы ещё может поверил <br />yyy: но вот жаба-программер - это ты по-моему загнул]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17722</guid>
<link>http://ibash.org.ru/quote.php?id=17722</link>
<title>Цитата #17722</title>
<pubDate>Thu, 15 Mar 2018 10:13:03 +0300</pubDate>
<description><![CDATA[xxx: из машинного перевода инструкции к бытовой технике: &quot;с 1 по 20 мая пройдут выборы ценностей в ранге&quot; (values within range 1 to 20 may be selected)]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17475</guid>
<link>http://ibash.org.ru/quote.php?id=17475</link>
<title>Цитата #17475</title>
<pubDate>Thu, 15 Mar 2018 10:05:41 +0300</pubDate>
<description><![CDATA[&lt;L29Ah&gt; [[ clang++ == *g++ ]] &amp;&amp; echo yay <br />&lt;L29Ah&gt; yay <br />&lt;Minoru&gt; «*g++»? Указатели в моём шелле?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17430</guid>
<link>http://ibash.org.ru/quote.php?id=17430</link>
<title>Цитата #17430</title>
<pubDate>Thu, 15 Mar 2018 09:59:50 +0300</pubDate>
<description><![CDATA[xxx: Все програмисты пое смерти в аду варятся в говнокоде :) <br />yyy: нет, в говнокоде мы варимся уже при жизни, в аду мы его рефакторим]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17419</guid>
<link>http://ibash.org.ru/quote.php?id=17419</link>
<title>Цитата #17419</title>
<pubDate>Thu, 15 Mar 2018 09:59:01 +0300</pubDate>
<description><![CDATA[&quot;OpenNET: QEMU/KVM и Xen подвержены уязвимости в коде эмуляции VGA&quot; <br /> <br />xx: proxmox на форуме написали что у них падает windows в среде эмуляции после патчей, ппц. <br />yy: Правильный патч. Многоцелевой.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17414</guid>
<link>http://ibash.org.ru/quote.php?id=17414</link>
<title>Цитата #17414</title>
<pubDate>Thu, 15 Mar 2018 09:58:30 +0300</pubDate>
<description><![CDATA[xxx: 600 Гб логов? У меня &gt;3 ТБ было. Дальше также место кончилось. Причина: случайно запись в лог внутри цикла написал, вместо вне него. Странно, что оно вообще работало.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17396</guid>
<link>http://ibash.org.ru/quote.php?id=17396</link>
<title>Цитата #17396</title>
<pubDate>Thu, 15 Mar 2018 09:56:44 +0300</pubDate>
<description><![CDATA[в офисе Apple: <br />- Может сделаем новый дизайн? <br />- Та не, мы меняли его уже пару лет назад, у кого еще идеи? <br />- А давайте исправим глюки на старых девайсах? <br />- Зачем? пусть покупают новые! <br />- Давайте добавим новые Emoji? <br />- Гениально! Так и сделаем!]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17394</guid>
<link>http://ibash.org.ru/quote.php?id=17394</link>
<title>Цитата #17394</title>
<pubDate>Thu, 15 Mar 2018 09:56:28 +0300</pubDate>
<description><![CDATA[xxx: Саша, речь не о том, что теоретически возможно прочесть произведения классики, и даже есть те, кто прочитывает. Мануал администратора фриБЗДи Ты тоже, наверно, одолел, и возможно, что на одном дыхании - Ты станешь на этом основании утверждать, что рекомый мануал есть выдающийся памятник словесности, написан легко, увлекательно и очень душевно? <br /> <br />yyy: Мануал администратора фрибсд, сравнительно с оракловой документацией, это выдающийся, мать его так, памятник словесности. Написан легко, увлекательно, и очень душевно.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17386</guid>
<link>http://ibash.org.ru/quote.php?id=17386</link>
<title>Цитата #17386</title>
<pubDate>Thu, 15 Mar 2018 09:55:30 +0300</pubDate>
<description><![CDATA[&gt; Компания Mail.Ru &lt;...&gt; будет предоставлять услуги коммерческой поддержки решений на базе свободной СУБД Tarantool <br /> <br />SELECT * FROM cars; <br /> <br />+------+--------------------+ <br />| id | name | <br />+------+--------------------+ <br />| 1 | &quot;Mazda CX-3&quot; | <br />| 2 | &quot;Audi Q1&quot; | <br />| 3 | &quot;BMW X1&quot; | <br />| 4 | &quot;Mazda CX-5&quot; | <br />| 5 | &quot;Cadillac XT5&quot; | <br />| NULL | &quot;Спутник@Mail.Ru&quot; | <br />| NULL | &quot;Guard@Mail.ru&quot; | <br />| NULL | &quot;Агент@Mail.ru &quot; | <br />| NULL | &quot;Mail.ru Updater&quot; | <br />+------+--------------------+]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17381</guid>
<link>http://ibash.org.ru/quote.php?id=17381</link>
<title>Цитата #17381</title>
<pubDate>Thu, 15 Mar 2018 09:54:50 +0300</pubDate>
<description><![CDATA[Дело в том, что любые новые фичи C++ должны пройти через комитет трёх фанатиков. <br /> <br />Первый фанатик обожает исключения. Всё собрание этот ворчливый старик кажется спящим, но в самый неудобный момент вскакаивает и перебивает говорящего криком «и тут мы бросаем исключение!» После этого конечно всё ломается. Этот старик мало кому нравится, но все вынуждены его терпеть. <br /> <br />Второй фанатик обожает шаблоны. Любую фичу он нежно оборачивает в шаблоны, которые заворачивает в шаблоны, которые заворачивает в шаблоны… пока она опять не сломается. В свободное время пытается написать программу по вычислению смысла жизни и вообще на этапе компиляции. <br /> <br />Третий фанатик, самый молодой, обожает всё параллельное. В отличие от других, он не критикует сразу. Он с энтузиазмом хватается за предложенную фичу, сразу переводит его в параллельность, убеждается что всё ломается и со вздохом «в наш век параллельного программирования так делать нельзя» отправляет фичу в корзину. Говорят, у него множество личностей, которые друг перебивают часто друга.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17375</guid>
<link>http://ibash.org.ru/quote.php?id=17375</link>
<title>Цитата #17375</title>
<pubDate>Thu, 15 Mar 2018 09:53:54 +0300</pubDate>
<description><![CDATA[клиент: Непингуется хост serv29. Посмотрите что с ним. <br />админ: Посмотрел на него, пинг появился. <br />клиент: А что с ним было? <br />админ: хз. я только посмотел. Могу объяснить это приниципом неопределенности: наблюдение влияет на результат.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17371</guid>
<link>http://ibash.org.ru/quote.php?id=17371</link>
<title>Цитата #17371</title>
<pubDate>Thu, 15 Mar 2018 09:53:46 +0300</pubDate>
<description><![CDATA[это гениально - вырядиться в костюм пингвина и ходить по улице, приставая к людям с вопросом: &quot;не хотите ли вы поговорить о линуксе?&quot; После чего вручать брошюрки про gentoo... хД]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17370</guid>
<link>http://ibash.org.ru/quote.php?id=17370</link>
<title>Цитата #17370</title>
<pubDate>Thu, 15 Mar 2018 09:53:28 +0300</pubDate>
<description><![CDATA[Обсуждение российского моноблока &quot;Таволга&quot;: <br />zzz: &quot;Как нам пояснили в компании &quot;???&quot;, &quot;не хотелось придумывать очередное безликое латинизированное название, обычно ассоциирующееся с IT, или аббревиатуру. Нам хотелось, чтобы название было узнаваемо русским, а не псевдо-западным, мелодичным и при этом не банальным вот так из множества вариантов выбрали Таволгу&quot;.&quot; <br /> <br />скоро так начнут и кодить на кирили...ах, ну да <br /> <br />xxx: Процессор Intel Core i5-5287U <br /> <br />yyy: Таволга, или Лаба&amp;#769;зник (лат. Filip&amp;#233;ndula) — род многолетних трав семейства Розовые (Rosaceae). Насчитывает 10—13 видов[3], произрастающих в умеренной зоне Северного полушария. <br />Садовое применение: <br />Великолепно отпугивает мух, комаров, слепней. <br /> <br />Походу попали в точку <br /> <br />zzz: Так ребята там здоровенный фумигатор на пятом коре запилили]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17369</guid>
<link>http://ibash.org.ru/quote.php?id=17369</link>
<title>Цитата #17369</title>
<pubDate>Thu, 15 Mar 2018 09:52:38 +0300</pubDate>
<description><![CDATA[Grother: все знают много историй про то, как шампунь или ещё какая хрень в ванной путалась с её кремом для депиляции. Но мало кому известны истории о том, как она мазала прыщи из симпатичного маленького тюбика с непонятным названием Pasta silikonova termoprzewodzaca.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17367</guid>
<link>http://ibash.org.ru/quote.php?id=17367</link>
<title>Цитата #17367</title>
<pubDate>Thu, 15 Mar 2018 09:52:20 +0300</pubDate>
<description><![CDATA[xxx: Свеже-родившийся анекдот - сколько нужно айтишников чтобы переткнуть сервер в другую подсеть? <br />xxx: Ответ: пять. Два тестировщика, два инжнера и админ. <br />xxx: Тестировщикам нужно но они не знают, инженеры знают но им нельзя. А админ пустил всех в серверную.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17363</guid>
<link>http://ibash.org.ru/quote.php?id=17363</link>
<title>Цитата #17363</title>
<pubDate>Thu, 15 Mar 2018 09:51:55 +0300</pubDate>
<description><![CDATA[Программисту по багам программы: <br />1.При выборе даты постоянно вылетает необрабатываемое исключение. <br />2.Если нажать на кнопку &quot;фильтр&quot; пока идёт создание фильтра - возникает необрабатываемое исключение. <br />Ответ: <br />П.1 Это ошибка не программы а её окружения, т.е. пофикси винду. <br />П.2 куда ты торопишься?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17359</guid>
<link>http://ibash.org.ru/quote.php?id=17359</link>
<title>Цитата #17359</title>
<pubDate>Thu, 15 Mar 2018 09:51:10 +0300</pubDate>
<description><![CDATA[Обсуждение ReactOS 0.4 на ЛОРе: <br /> <br />Oxdeadbeef: Оно может в x86_64? <br /> <br />Jedi-to-be: Может, но пока нет.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17358</guid>
<link>http://ibash.org.ru/quote.php?id=17358</link>
<title>Цитата #17358</title>
<pubDate>Thu, 15 Mar 2018 09:51:04 +0300</pubDate>
<description><![CDATA[&lt;dsmirnov&gt; на супермикро и в отвратительных шкафах ..... а вы лабутены, лабутены ....]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17357</guid>
<link>http://ibash.org.ru/quote.php?id=17357</link>
<title>Цитата #17357</title>
<pubDate>Thu, 15 Mar 2018 09:50:55 +0300</pubDate>
<description><![CDATA[разраб: деплоим. 20 пендингов <br />тестер: боже, сохрани <br />разраб: не поможет, место на облаке закончилось]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17356</guid>
<link>http://ibash.org.ru/quote.php?id=17356</link>
<title>Цитата #17356</title>
<pubDate>Thu, 15 Mar 2018 09:50:51 +0300</pubDate>
<description><![CDATA[xxx: Программно-аппаратная платформа удаленного администрирования автоматизированных систем. Сокращенно - ПАПУАС]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17354</guid>
<link>http://ibash.org.ru/quote.php?id=17354</link>
<title>Цитата #17354</title>
<pubDate>Thu, 15 Mar 2018 09:50:41 +0300</pubDate>
<description><![CDATA[Nick&gt; Что вы вообще понимаете в тормозных серверах <br />Nick&gt; Мой пишет “System information disabled due to load higher than 1.0” <br />Nick&gt; Вот только при этом вилка локалки рядом на столе лежит, вынутая из сетевухи <br />Nick&gt; Для селерона 400, в который через PCI-адаптер SATA воткнут терабайтник… <br />Nick&gt; да для него просто дышать — и то уже физкультура]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17353</guid>
<link>http://ibash.org.ru/quote.php?id=17353</link>
<title>Цитата #17353</title>
<pubDate>Thu, 15 Mar 2018 09:49:52 +0300</pubDate>
<description><![CDATA[Мой мир никогда не станет прежним. Сегодня я узнал, что RoHS это не название китайской фирмы по выпуску электронных компонентов, а директива по содержанию в них вредных веществ]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17316</guid>
<link>http://ibash.org.ru/quote.php?id=17316</link>
<title>Цитата #17316</title>
<pubDate>Thu, 15 Mar 2018 09:47:42 +0300</pubDate>
<description><![CDATA[Елена: Дали строителям проект, они строили-строили и наконец построили. Приезжает заказчик. В грунте выкопана цилиндрическая яма метров 15 в глубину. На дне сияет прожектор. Заказчик переворачивает чертёж на 180 градусов и говорит: “Всё хорошо, но по проекту здесь должен был быть МАЯК”. <br />Dmitry: Боженьки мои ))))) <br />Елена: немножко похоже на историю из жизни программистов <br />Dmitry: ага ) только программист может потом решить, что будет легче все корабли сделать подземными, чем переделывать маяк.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17314</guid>
<link>http://ibash.org.ru/quote.php?id=17314</link>
<title>Цитата #17314</title>
<pubDate>Thu, 15 Mar 2018 09:46:52 +0300</pubDate>
<description><![CDATA[Kikimorra: Изучаю джаваскрипт на онлайн-курсах. Чувак у доски рассказывает, как удалять ноды. Приводимая в пример веб-страница выглядит так: <br /> <br />It&#039;s a nice day! <br />и кнопка <br />Delete all children! <br /> <br />Прям кодишь и чувствуешь, как рога с хвостом прорастают &gt;.&lt;]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17312</guid>
<link>http://ibash.org.ru/quote.php?id=17312</link>
<title>Цитата #17312</title>
<pubDate>Thu, 15 Mar 2018 09:46:33 +0300</pubDate>
<description><![CDATA[xxx:после апдейта винда взяла и переставила панель задач слева обратно вниз <br />xxx:мол, не выёбывайся <br />ххх:тебе не убунта]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17306</guid>
<link>http://ibash.org.ru/quote.php?id=17306</link>
<title>Цитата #17306</title>
<pubDate>Thu, 15 Mar 2018 09:46:21 +0300</pubDate>
<description><![CDATA[aaa: Есть же специальные для тачскринов, там кончики пальцев сделаны из проводящего материала. <br />bbb: Оо, как они называются? А то я видел только вязаные igloves, которые рвутся через месяц использования, И в мороз в них не походишь. <br />ccc: Купите металлизированные нитки и сделайте несколько стежков под подушечками пальцев. Начинайте прошивать изнутри оставив конец нитки после узелка подлиннее, чтобы обеспечить лучшую проводимость. <br />ddd: — Как ты работаешь с айпадом в перчатках? <br />— Я их перепрошил.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17304</guid>
<link>http://ibash.org.ru/quote.php?id=17304</link>
<title>Цитата #17304</title>
<pubDate>Thu, 15 Mar 2018 09:45:51 +0300</pubDate>
<description><![CDATA[&lt;&gt; нет ничего приятнее теплого лампового диалапа... <br />&lt;&gt; когда жужжание фрезы сливается с звуками хэндшейка в гармоничную мелодию?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17297</guid>
<link>http://ibash.org.ru/quote.php?id=17297</link>
<title>Цитата #17297</title>
<pubDate>Thu, 15 Mar 2018 09:44:04 +0300</pubDate>
<description><![CDATA[[15:15:56] r@ttler: говно успешно прилеплено и оттестировано. говно оказалось говном <br />[15:16:05] ZimM: внезапно <br />[15:17:30] r@ttler: ну я пока его прилеплял мне аж привидилась картина: замок разраба говна. дорога к нему увенчана костылями. как вот копья вешали с черепами врагов, так тут костыли с черепами разрабов <br />[15:17:59] r@ttler: и табличка на воротах &quot;оставь свой мозг, всяк сюда входящий&quot;]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17291</guid>
<link>http://ibash.org.ru/quote.php?id=17291</link>
<title>Цитата #17291</title>
<pubDate>Thu, 15 Mar 2018 09:42:11 +0300</pubDate>
<description><![CDATA[Если бы врачи были ИТ-шниками: <br /> <br />- У меня не работает клавиатура. <br /> <br />- Сдайте пробы на подклавиатурный сахар и лактозу, сделайте рентген и функциональный тест клавиш.... <br />[через 2 месяца и 20 тысяч рублей]...Действительно, обнаружена карамельная бляшка под пробелом. <br /> <br />- Что же делать? <br /> <br />- Вам показана консервативная терапия: ежедневно постукивайте 15 минут по перевернутой клавиатуре, затем 15 минут протирайте клавишу спиртом, потом разрабатывайте клавишу вручную. До конца срока службы вашего системного блока старайтесь как можно реже и аккуратнее пользоваться пробелом. Поставьте виртуальную клавиатуру и печатайте мышкой.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17285</guid>
<link>http://ibash.org.ru/quote.php?id=17285</link>
<title>Цитата #17285</title>
<pubDate>Thu, 15 Mar 2018 09:40:57 +0300</pubDate>
<description><![CDATA[ZimM: нуу... with great power comes great responsibility <br />r@ttler: great chances to shoot your own leg <br />ZimM: ну это да. другое дело, что для этого все равно нужно постараться <br />r@ttler: work hard to shoot your own leg? <br />r@ttler: ну тогда ты совсем лол <br />r@ttler: впадло было делать по-нормальному, потому помучался и таки сделал через жопу <br />ZimM: ну, я думал, что отстрелю себе фалангу мизинца, а оторвал пол-туловища, потому что пол-туловища мне показались похожими на фалангу мизинца...]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17280</guid>
<link>http://ibash.org.ru/quote.php?id=17280</link>
<title>Цитата #17280</title>
<pubDate>Thu, 15 Mar 2018 09:37:53 +0300</pubDate>
<description><![CDATA[xxx: Вот говорят иногда &quot;зоопарк браузеров&quot; (операционных систем, железок и так далее), а я тут внезапно понял, что у меня самый настоящий бордель виртуальных машин. Потому что их у меня четыре, названы женскими именами, чтобы быстро различать, и я с ними трахаюсь. Причём в данный момент со всеми четырьмя одновременно, потому что делаю лабораторную по сетям.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17279</guid>
<link>http://ibash.org.ru/quote.php?id=17279</link>
<title>Цитата #17279</title>
<pubDate>Thu, 15 Mar 2018 09:37:50 +0300</pubDate>
<description><![CDATA[Val: США провели третье и последнее испытание новой атомной бомбы B61-12. Бомбу без заряда, в соответствии с международным договором о запрете ядерных взрывов, сбросили с истребителя F-15E на полигоне &quot;Тонопа&quot; в Неваде 20 октября. <br />Val: какое интересное испытание. При ударе о землю она выбросила флажок &quot;БУМ&quot;? <br />Кир: Отправила в твиттер &quot;БДЫЩ!&quot; - она жы высокотехнологичная]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17276</guid>
<link>http://ibash.org.ru/quote.php?id=17276</link>
<title>Цитата #17276</title>
<pubDate>Thu, 15 Mar 2018 09:29:42 +0300</pubDate>
<description><![CDATA[dimgel: (ссылка на ленту.ру) &quot;Linux.Encoder.1 — относится к классу троянцев-шифровальщиков. После запуска с правами администратора...&quot; <br />Бгг. Ещё сто лет назад шутка ходила про линуксовые вирусы: &quot;распакуйте меня пожалуйста и пропишите в автозапуск демоном от имени рута&quot;. <br />dimgel: Я смотрю, ни хера не меняется в этой жизни. <br />garik: на ещё надо пару патчей найти и накатить <br />garik: иначе не скомпилится <br />dimgel: обязательно <br />dimgel: причём для разных дистров патчи будут разные <br />dimgel: Народ! Как пропатчить Linux.Encoder.1 под FreeBSD?!]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17272</guid>
<link>http://ibash.org.ru/quote.php?id=17272</link>
<title>Цитата #17272</title>
<pubDate>Thu, 15 Mar 2018 09:28:44 +0300</pubDate>
<description><![CDATA[xxx: когда читаю такие ограничения дурацкие, хочется спросить, каким местом пишутся драйвера <br />yyy: Но тут хоть плюс, что он сам не падает, а ошибку только выдаёт. <br />xxx: андроид разработчик... <br />xxx: ну хорошо хоть не падает вместе с системой!! <br />xxx: а то что всё в говне и не работает это мелочи <br />xxx: дальше будет &quot;ну хоть не сносит систему&quot; <br />xxx: &quot;ну хоть не выжигает гпу&quot; <br />yyy: &quot;ну, хоть не выжигает глаза&quot; <br />xxx: &quot;ну хоть не подключается к скайнету и не выжигает поверхность планеты&quot; <br />yyy: &quot;ну, хотя бы не уничтожает вселенную&quot; <br />xxx: вот видишь, повезло-то как!]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17259</guid>
<link>http://ibash.org.ru/quote.php?id=17259</link>
<title>Цитата #17259</title>
<pubDate>Thu, 15 Mar 2018 09:25:43 +0300</pubDate>
<description><![CDATA[обсуждение странного результата трейсроута <br />[19:27:36] ZimM: пробил по геоип. реально Europe, но если по координатам глянуть - то швейцария <br />[19:28:01] r@ttler: и пробей соседний. реально сша? <br />[19:28:30] ZimM: реально сша <br />[19:28:49] r@ttler: ну значит лол <br />[19:29:01] r@ttler: сто раз туда-сюда по трансатлантике? <br />[19:29:08] ZimM: ну а хуле <br />[19:29:14] r@ttler: вокруг света за 80 хопов?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17236</guid>
<link>http://ibash.org.ru/quote.php?id=17236</link>
<title>Цитата #17236</title>
<pubDate>Thu, 15 Mar 2018 09:23:51 +0300</pubDate>
<description><![CDATA[xxx: &quot;Как перестать юзать чужой код и научиться прогать самостоятельно&quot;, новый бестселлер Алан Карра]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17235</guid>
<link>http://ibash.org.ru/quote.php?id=17235</link>
<title>Цитата #17235</title>
<pubDate>Thu, 15 Mar 2018 09:23:49 +0300</pubDate>
<description><![CDATA[xx: Флеш умер, google его выпилит скоро :) <br /> <br />yy: Джобс тоже так говорил) <br /> <br />zz: Джобса уже бог (или ктулху, или матрица - кому что нравится) выпилил, а флэш еще барахтается <br /> <br />tt: Ну разве не ясно, что это флеш его и выпилил?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17233</guid>
<link>http://ibash.org.ru/quote.php?id=17233</link>
<title>Цитата #17233</title>
<pubDate>Thu, 15 Mar 2018 09:23:33 +0300</pubDate>
<description><![CDATA[К новости &quot;Отечественный защищённый Linux-дистрибутив Заря готов к внедрению&quot;: <br /> <br />xxx: Теперь будет сборка-разборка не только автомата, но и ядра. Норматив 3 минуты.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17230</guid>
<link>http://ibash.org.ru/quote.php?id=17230</link>
<title>Цитата #17230</title>
<pubDate>Thu, 15 Mar 2018 09:22:21 +0300</pubDate>
<description><![CDATA[С Хабра: <br />Смотрели Last Exile, где были шахматы для воздушных кораблей — с фиксацией? Вот термос для поезда. У него горлышко как у чайника, но оно перекрыто. Кнопка на ручке открывает возможность лить из термоса наружу. Это классика страховки от ошибок. Пользоваться потенциально опасной функцией можно только сознательно. <br />[...] <br />Это ещё и защита от дурака, в частности, важная для техники безопасности. У нас на производстве есть станок, который умеет прошибать гильотинным ножиком сразу огромную пачку бумаги. Так вот, чтобы его запустить нужно: <br /> <br />1) Положить бумагу под датчик бумаги <br />2) Положить левую руку на левую пусковую кнопку далеко слева <br />3) Правую руку — на правую пусковую кнопку далеко справа <br />4) Нажать педаль ногой (в этом положении физически невозможно засунуть голову в рабочую область станка) <br />5) Нажать обе пусковые кнопки одновременно <br />6) Но, видимо, рабочие научились действовать вдвоём или блокировать кнопки — и поэтому ещё нужно убрать всё из рабочей зоны, чтобы инфракрасные лучи не пересекались. Только после этого случится пуск.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17226</guid>
<link>http://ibash.org.ru/quote.php?id=17226</link>
<title>Цитата #17226</title>
<pubDate>Thu, 15 Mar 2018 09:20:48 +0300</pubDate>
<description><![CDATA[Архитекторы, емае, это источник нескончаемого умиления. Планерка, разбор какого-то легаси модуля. Говорят переписывать будем. Архитектор его анализировал неделю. Читал там код, компилил что-то, виртуалок поднял кучу, думал спеку, ходил курить каждые 15 и посадил картридж в принтере. Вот выходит этот мегачеловече к вайтборду докладывать комманде об устройстве этой вундервафли. Берет маркер, долго думает, рисует кружок. Еще думает, закрашивает. Думает еще, рисует входящую стрелку. Потом еще исходящую. Поворачивается к комманде: <br /> <br />-- В общем, коллеги, это жопа. Работает все через нее. А теперь посмотрим дему и обсудим баги. <br /> <br />Я вообще удивлен что в они в свой UML до сих пор стандартный символ не добавили.]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17227</guid>
<link>http://ibash.org.ru/quote.php?id=17227</link>
<title>Цитата #17227</title>
<pubDate>Thu, 15 Mar 2018 09:20:46 +0300</pubDate>
<description><![CDATA[xxx: О, Боже! Я начинаю получать кайф от пользования линуксом... Развернул убунту, понадобилось пару программ поставить, я ему пишу в консоли аптгет инсталл со списком желаемого, а он сам ищет, ставит и настраивает, и не надо дистрибутивы искать, по сайтам лазить, качать...]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17223</guid>
<link>http://ibash.org.ru/quote.php?id=17223</link>
<title>Цитата #17223</title>
<pubDate>Thu, 15 Mar 2018 09:19:38 +0300</pubDate>
<description><![CDATA[xxx: 40 Ватт, бывали у АМД и помощнее, если вы понимаете... <br />yyy: у меня был пентиум D <br />yyy: этим все сказано <br />yyy: когда я запускал эклипс - зимой наступало лето <br />zzz: у меня тоже греет комнату хорошо :) <br />zzz: сейчас 80 градусов <br />xxx: да это не комната - это баня ))) <br />yyy: живучий однако <br />yyy: мой спалил три мамки <br />yyy: у всех диагноз - микротрещины <br />yyy: это была середина 2000х, обычный офисный корпус, потолок регистрировал примерно 120 градусов <br />yyy: я летом морозил лед и крошил его в миску перед воздухозаборным отверстием <br />yyy: и менял его раз в час <br />yyy: когда грузилась моя винда, запотевали окна соседних домов <br />yyy: однажды, когда я архивировал сериал на флешку, ученые отметили глобальное потепление на 5 градусов <br />yyy: каждый раз, как я компилировал проект, в антарктиде семейство пингвинов оставалось без гнезда <br />xxx: мне кажется, что Чак тебе начинает завидовать <br />yyy: чак приходил ко мне домой разгоревать свой завтрак <br />yyy: свой процессор я отдал ученым, и в итоге он лег в основу ИТЭР]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17224</guid>
<link>http://ibash.org.ru/quote.php?id=17224</link>
<title>Цитата #17224</title>
<pubDate>Thu, 15 Mar 2018 09:19:36 +0300</pubDate>
<description><![CDATA[— Человек, который работает в ИБ, не знает, что такое NDA? <br />— Может быть ему по NDA нельзя говорить, что знает?]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17221</guid>
<link>http://ibash.org.ru/quote.php?id=17221</link>
<title>Цитата #17221</title>
<pubDate>Thu, 15 Mar 2018 09:17:22 +0300</pubDate>
<description><![CDATA[duzorg: <br />У кондиционера заклинило дренаж и вся вода вылилась внутрь серверной. Благо я туда случайно зашёл. Там серваки стояли на деревянной столешнице, а её водой размыло, она посередине где то на 15-20см прогнулась. Еще бы немного и рухнули бы все серваки на пол. Тысяч на 100 попали бы... <br />Или скорее даже больше чем на 100. <br />duzorg: <br />А в серверную я зашёл, потому что микротик завис. А микротик впервые за 4 года завис. %) <br />Funkryer: <br />вот так повезло <br />отличный был бы отзыв о микротике =) <br />мол завис 1 раз за 4 года и то только для того, чтобы спасти наши серваки <br />duzorg: <br />Не только работает стабильно, но и заботится о стабильной работе рядом находящегося оборудования ))) <br />duzorg: <br />На самом деле может его от большой влажности чё нить клинануло... хз... <br />Funkryer: <br />ну, начал, не порть магию!]]></description>
</item>
<item>
<guid>http://ibash.org.ru/quote.php?id=17220</guid>
<link>http://ibash.org.ru/quote.php?id=17220</link>
<title>Цитата #17220</title>
<pubDate>Thu, 15 Mar 2018 09:16:48 +0300</pubDate>
<description><![CDATA[xxx: конструктор запросов как всегда гентален <br />xxx: :( <br />xxx: хм хотел написать гениален, но генитален тоже пойдет]]></description>
</item>
</channel>
</rss>

View File

@ -6,15 +6,14 @@ package cookie // import "miniflux.app/v2/internal/http/cookie"
import (
"net/http"
"time"
"miniflux.app/v2/internal/config"
)
// Cookie names.
const (
CookieAppSessionID = "MinifluxAppSessionID"
CookieUserSessionID = "MinifluxUserSessionID"
// Cookie duration in days.
cookieDuration = 30
)
// New creates a new cookie.
@ -25,7 +24,7 @@ func New(name, value string, isHTTPS bool, path string) *http.Cookie {
Path: basePath(path),
Secure: isHTTPS,
HttpOnly: true,
Expires: time.Now().Add(cookieDuration * 24 * time.Hour),
Expires: time.Now().Add(time.Duration(config.Opts.CleanupRemoveSessionsDays()) * 24 * time.Hour),
SameSite: http.SameSiteLaxMode,
}
}

View File

@ -30,20 +30,13 @@ func FindClientIP(r *http.Request) string {
return FindRemoteIP(r)
}
// FindRemoteIP returns remote client IP address.
// FindRemoteIP returns remote client IP address without considering HTTP headers.
func FindRemoteIP(r *http.Request) string {
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
remoteIP = r.RemoteAddr
}
remoteIP = dropIPv6zone(remoteIP)
// When listening on a Unix socket, RemoteAddr is empty.
if remoteIP == "" {
remoteIP = "127.0.0.1"
}
return remoteIP
return dropIPv6zone(remoteIP)
}
func dropIPv6zone(address string) string {

View File

@ -104,20 +104,20 @@ func TestClientIPWithBothHeaders(t *testing.T) {
}
}
func TestClientIPWithNoRemoteAddress(t *testing.T) {
r := &http.Request{}
func TestClientIPWithUnixSocketRemoteAddress(t *testing.T) {
r := &http.Request{RemoteAddr: "@"}
if ip := FindClientIP(r); ip != "127.0.0.1" {
if ip := FindClientIP(r); ip != "@" {
t.Fatalf(`Unexpected result, got: %q`, ip)
}
}
func TestClientIPWithoutRemoteAddrAndBothHeaders(t *testing.T) {
func TestClientIPWithUnixSocketRemoteAddrAndBothHeaders(t *testing.T) {
headers := http.Header{}
headers.Set("X-Forwarded-For", "203.0.113.195, 70.41.3.18, 150.172.238.178")
headers.Set("X-Real-Ip", "192.168.122.1")
r := &http.Request{RemoteAddr: "", Header: headers}
r := &http.Request{RemoteAddr: "@", Header: headers}
if ip := FindClientIP(r); ip != "203.0.113.195" {
t.Fatalf(`Unexpected result, got: %q`, ip)

View File

@ -3,7 +3,12 @@
package request // import "miniflux.app/v2/internal/http/request"
import "net/http"
import (
"net/http"
"strconv"
"miniflux.app/v2/internal/model"
)
// ContextKey represents a context key.
type ContextKey int
@ -24,10 +29,21 @@ const (
FlashMessageContextKey
FlashErrorMessageContextKey
PocketRequestTokenContextKey
LastForceRefreshContextKey
ClientIPContextKey
GoogleReaderToken
WebAuthnDataContextKey
)
func WebAuthnSessionData(r *http.Request) *model.WebAuthnSession {
if v := r.Context().Value(WebAuthnDataContextKey); v != nil {
if value, valid := v.(model.WebAuthnSession); valid {
return &value
}
}
return nil
}
// GoolgeReaderToken returns the google reader token if it exists.
func GoolgeReaderToken(r *http.Request) string {
return getContextStringValue(r, GoogleReaderToken)
@ -114,6 +130,16 @@ func PocketRequestToken(r *http.Request) string {
return getContextStringValue(r, PocketRequestTokenContextKey)
}
// LastForceRefresh returns the last force refresh timestamp.
func LastForceRefresh(r *http.Request) int64 {
jsonStringValue := getContextStringValue(r, LastForceRefreshContextKey)
timestamp, err := strconv.ParseInt(jsonStringValue, 10, 64)
if err != nil {
return 0
}
return timestamp
}
// ClientIP returns the client IP address stored in the context.
func ClientIP(r *http.Request) string {
return getContextStringValue(r, ClientIPContextKey)
@ -121,39 +147,27 @@ func ClientIP(r *http.Request) string {
func getContextStringValue(r *http.Request, key ContextKey) string {
if v := r.Context().Value(key); v != nil {
value, valid := v.(string)
if !valid {
return ""
if value, valid := v.(string); valid {
return value
}
return value
}
return ""
}
func getContextBoolValue(r *http.Request, key ContextKey) bool {
if v := r.Context().Value(key); v != nil {
value, valid := v.(bool)
if !valid {
return false
if value, valid := v.(bool); valid {
return value
}
return value
}
return false
}
func getContextInt64Value(r *http.Request, key ContextKey) int64 {
if v := r.Context().Value(key); v != nil {
value, valid := v.(int64)
if !valid {
return 0
if value, valid := v.(int64); valid {
return value
}
return value
}
return 0
}

View File

@ -12,6 +12,8 @@ import (
"net/http"
"strings"
"time"
"github.com/andybalholm/brotli"
)
const compressionThreshold = 1024
@ -96,7 +98,6 @@ func (b *Builder) Write() {
}
func (b *Builder) writeHeaders() {
b.headers["X-XSS-Protection"] = "1; mode=block"
b.headers["X-Content-Type-Options"] = "nosniff"
b.headers["X-Frame-Options"] = "DENY"
b.headers["Referrer-Policy"] = "no-referrer"
@ -111,8 +112,15 @@ func (b *Builder) writeHeaders() {
func (b *Builder) compress(data []byte) {
if b.enableCompression && len(data) > compressionThreshold {
acceptEncoding := b.r.Header.Get("Accept-Encoding")
switch {
case strings.Contains(acceptEncoding, "br"):
b.headers["Content-Encoding"] = "br"
b.writeHeaders()
brotliWriter := brotli.NewWriterV2(b.w, brotli.DefaultCompression)
defer brotliWriter.Close()
brotliWriter.Write(data)
return
case strings.Contains(acceptEncoding, "gzip"):
b.headers["Content-Encoding"] = "gzip"
b.writeHeaders()

View File

@ -28,7 +28,6 @@ func TestResponseHasCommonHeaders(t *testing.T) {
resp := w.Result()
headers := map[string]string{
"X-XSS-Protection": "1; mode=block",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
}
@ -229,7 +228,7 @@ func TestBuildResponseWithCachingAndEtag(t *testing.T) {
}
}
func TestBuildResponseWithGzipCompression(t *testing.T) {
func TestBuildResponseWithBrotliCompression(t *testing.T) {
body := strings.Repeat("a", compressionThreshold+1)
r, err := http.NewRequest("GET", "/", nil)
r.Header.Set("Accept-Encoding", "gzip, deflate, br")
@ -246,6 +245,30 @@ func TestBuildResponseWithGzipCompression(t *testing.T) {
handler.ServeHTTP(w, r)
resp := w.Result()
expected := "br"
actual := resp.Header.Get("Content-Encoding")
if actual != expected {
t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
}
}
func TestBuildResponseWithGzipCompression(t *testing.T) {
body := strings.Repeat("a", compressionThreshold+1)
r, err := http.NewRequest("GET", "/", nil)
r.Header.Set("Accept-Encoding", "gzip, deflate")
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
New(w, r).WithBody(body).Write()
})
handler.ServeHTTP(w, r)
resp := w.Result()
expected := "gzip"
actual := resp.Header.Get("Content-Encoding")
if actual != expected {

View File

@ -268,6 +268,12 @@ func isAllowedToAccessMetricsEndpoint(r *http.Request) bool {
}
}
remoteIP := request.FindRemoteIP(r)
if remoteIP == "@" {
// This indicates a request sent via a Unix socket, always consider these trusted.
return true
}
for _, cidr := range config.Opts.MetricsAllowedNetworks() {
_, network, err := net.ParseCIDR(cidr)
if err != nil {
@ -283,7 +289,7 @@ func isAllowedToAccessMetricsEndpoint(r *http.Request) bool {
// We use r.RemoteAddr in this case because HTTP headers like X-Forwarded-For can be easily spoofed.
// The recommendation is to use HTTP Basic authentication.
if network.Contains(net.ParseIP(request.FindRemoteIP(r))) {
if network.Contains(net.ParseIP(remoteIP)) {
return true
}
}

View File

@ -10,12 +10,17 @@ import (
"miniflux.app/v2/internal/integration/apprise"
"miniflux.app/v2/internal/integration/espial"
"miniflux.app/v2/internal/integration/instapaper"
"miniflux.app/v2/internal/integration/linkace"
"miniflux.app/v2/internal/integration/linkding"
"miniflux.app/v2/internal/integration/linkwarden"
"miniflux.app/v2/internal/integration/matrixbot"
"miniflux.app/v2/internal/integration/notion"
"miniflux.app/v2/internal/integration/nunuxkeeper"
"miniflux.app/v2/internal/integration/omnivore"
"miniflux.app/v2/internal/integration/pinboard"
"miniflux.app/v2/internal/integration/pocket"
"miniflux.app/v2/internal/integration/raindrop"
"miniflux.app/v2/internal/integration/readeck"
"miniflux.app/v2/internal/integration/readwise"
"miniflux.app/v2/internal/integration/shaarli"
"miniflux.app/v2/internal/integration/shiori"
@ -179,6 +184,30 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
}
}
if userIntegrations.LinkAceEnabled {
slog.Debug("Sending entry to LinkAce",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := linkace.NewClient(
userIntegrations.LinkAceURL,
userIntegrations.LinkAceAPIKey,
userIntegrations.LinkAceTags,
userIntegrations.LinkAcePrivate,
userIntegrations.LinkAceCheckDisabled,
)
if err := client.AddURL(entry.URL, entry.Title); err != nil {
slog.Error("Unable to send entry to LinkAce",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
if userIntegrations.LinkdingEnabled {
slog.Debug("Sending entry to Linkding",
slog.Int64("user_id", userIntegrations.UserID),
@ -202,6 +231,50 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
}
}
if userIntegrations.LinkwardenEnabled {
slog.Debug("Sending entry to linkwarden",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := linkwarden.NewClient(
userIntegrations.LinkwardenURL,
userIntegrations.LinkwardenAPIKey,
)
if err := client.CreateBookmark(entry.URL, entry.Title); err != nil {
slog.Error("Unable to send entry to Linkwarden",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
if userIntegrations.ReadeckEnabled {
slog.Debug("Sending entry to Readeck",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := readeck.NewClient(
userIntegrations.ReadeckURL,
userIntegrations.ReadeckAPIKey,
userIntegrations.ReadeckLabels,
userIntegrations.ReadeckOnlyURL,
)
if err := client.CreateBookmark(entry.URL, entry.Title, entry.Content); err != nil {
slog.Error("Unable to send entry to Readeck",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
if userIntegrations.ReadwiseEnabled {
slog.Debug("Sending entry to Readwise",
slog.Int64("user_id", userIntegrations.UserID),
@ -287,6 +360,42 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
)
}
}
if userIntegrations.OmnivoreEnabled {
slog.Debug("Sending entry to Omnivore",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := omnivore.NewClient(userIntegrations.OmnivoreAPIKey, userIntegrations.OmnivoreURL)
if err := client.SaveUrl(entry.URL); err != nil {
slog.Error("Unable to send entry to Omnivore",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
if userIntegrations.RaindropEnabled {
slog.Debug("Sending entry to Raindrop",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)
client := raindrop.NewClient(userIntegrations.RaindropToken, userIntegrations.RaindropCollectionID, userIntegrations.RaindropTags)
if err := client.CreateRaindrop(entry.URL, entry.Title); err != nil {
slog.Error("Unable to send entry to Raindrop",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}
}
// PushEntries pushes a list of entries to activated third-party providers during feed refreshes.

View File

@ -0,0 +1,83 @@
package linkace
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
baseURL string
apiKey string
tags string
private bool
checkDisabled bool
}
func NewClient(baseURL, apiKey, tags string, private bool, checkDisabled bool) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey, tags: tags, private: private, checkDisabled: checkDisabled}
}
func (c *Client) AddURL(entryURL, entryTitle string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("linkace: missing base URL or API key")
}
tagsSplitFn := func(c rune) bool {
return c == ',' || c == ' '
}
apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/v1/links")
if err != nil {
return fmt.Errorf("linkace: invalid API endpoint: %v", err)
}
requestBody, err := json.Marshal(&createItemRequest{
Url: entryURL,
Title: entryTitle,
Tags: strings.FieldsFunc(c.tags, tagsSplitFn),
Private: c.private,
CheckDisabled: c.checkDisabled,
})
if err != nil {
return fmt.Errorf("linkace: unable to encode request body: %v", err)
}
request, err := http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return fmt.Errorf("linkace: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("Authorization", "Bearer "+c.apiKey)
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("linkace: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("linkace: unable to create item: url=%s status=%d", apiEndpoint, response.StatusCode)
}
return nil
}
type createItemRequest struct {
Title string `json:"title,omitempty"`
Url string `json:"url"`
Tags []string `json:"tags,omitempty"`
Private bool `json:"is_private,omitempty"`
CheckDisabled bool `json:"check_disabled,omitempty"`
}

View File

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package linkwarden // import "miniflux.app/v2/internal/integration/linkwarden"
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
baseURL string
apiKey string
}
func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}
func (c *Client) CreateBookmark(entryURL, entryTitle string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("linkwarden: missing base URL or API key")
}
apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/v1/links")
if err != nil {
return fmt.Errorf(`linkwarden: invalid API endpoint: %v`, err)
}
requestBody, err := json.Marshal(&linkwardenBookmark{
Url: entryURL,
Name: "",
Description: "",
Tags: []string{},
Collection: map[string]interface{}{},
})
if err != nil {
return fmt.Errorf("linkwarden: unable to encode request body: %v", err)
}
request, err := http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return fmt.Errorf("linkwarden: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.AddCookie(&http.Cookie{Name: "__Secure-next-auth.session-token", Value: c.apiKey})
request.AddCookie(&http.Cookie{Name: "next-auth.session-token", Value: c.apiKey})
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("linkwarden: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("linkwarden: unable to create link: url=%s status=%d", apiEndpoint, response.StatusCode)
}
return nil
}
type linkwardenBookmark struct {
Url string `json:"url"`
Name string `json:"name"`
Description string `json:"description"`
Tags []string `json:"tags"`
Collection map[string]interface{} `json:"collection"`
}

View File

@ -10,7 +10,7 @@ import (
"miniflux.app/v2/internal/model"
)
// PushEntry pushes entries to matrix chat using integration settings provided
// PushEntries pushes entries to matrix chat using integration settings provided
func PushEntries(feed *model.Feed, entries model.Entries, matrixBaseURL, matrixUsername, matrixPassword, matrixRoomID string) error {
client := NewClient(matrixBaseURL)
discovery, err := client.DiscoverEndpoints()
@ -28,7 +28,7 @@ func PushEntries(feed *model.Feed, entries model.Entries, matrixBaseURL, matrixU
for _, entry := range entries {
textMessages = append(textMessages, fmt.Sprintf(`[%s] %s - %s`, feed.Title, entry.Title, entry.URL))
formattedTextMessages = append(formattedTextMessages, fmt.Sprintf(`<li><strong>%s</strong>: <a href="%s">%s</a></li>`, feed.Title, entry.URL, entry.Title))
formattedTextMessages = append(formattedTextMessages, fmt.Sprintf(`<li><strong>%s</strong>: <a href=%q>%s</a></li>`, feed.Title, entry.URL, entry.Title))
}
_, err = client.SendFormattedTextMessage(

View File

@ -0,0 +1,125 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package omnivore // import "miniflux.app/v2/internal/integration/omnivore"
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"miniflux.app/v2/internal/crypto"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
const defaultApiEndpoint = "https://api-prod.omnivore.app/api/graphql"
var mutation = `
mutation SaveUrl($input: SaveUrlInput!) {
saveUrl(input: $input) {
... on SaveSuccess {
url
clientRequestId
}
... on SaveError {
errorCodes
message
}
}
}
`
type SaveUrlInput struct {
ClientRequestId string `json:"clientRequestId"`
Source string `json:"source"`
Url string `json:"url"`
}
type errorResponse struct {
Errors []struct {
Message string `json:"message"`
} `json:"errors"`
}
type successResponse struct {
Data struct {
SaveUrl struct {
Url string `json:"url"`
ClientRequestId string `json:"clientRequestId"`
} `json:"saveUrl"`
} `json:"data"`
}
type Client interface {
SaveUrl(url string) error
}
type client struct {
wrapped *http.Client
apiEndpoint string
apiToken string
}
func NewClient(apiToken string, apiEndpoint string) Client {
if apiEndpoint == "" {
apiEndpoint = defaultApiEndpoint
}
return &client{wrapped: &http.Client{Timeout: defaultClientTimeout}, apiEndpoint: apiEndpoint, apiToken: apiToken}
}
func (c *client) SaveUrl(url string) error {
var payload = map[string]interface{}{
"query": mutation,
"variables": map[string]interface{}{
"input": map[string]interface{}{
"clientRequestId": crypto.GenerateUUID(),
"source": "api",
"url": url,
},
},
}
b, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, c.apiEndpoint, bytes.NewReader(b))
if err != nil {
return err
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Miniflux/"+version.Version)
resp, err := c.wrapped.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
b, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("omnivore: failed to parse response: %s", err)
}
if resp.StatusCode >= 400 {
var errResponse errorResponse
if err = json.Unmarshal(b, &errResponse); err != nil {
return fmt.Errorf("omnivore: failed to save URL: status=%d %s", resp.StatusCode, string(b))
}
return fmt.Errorf("omnivore: failed to save URL: status=%d %s", resp.StatusCode, errResponse.Errors[0].Message)
}
var successReponse successResponse
if err = json.Unmarshal(b, &successReponse); err != nil {
return fmt.Errorf("omnivore: failed to parse response, however the request appears successful, is the url correct?: status=%d %s", resp.StatusCode, string(b))
}
return nil
}

View File

@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package raindrop // import "miniflux.app/v2/internal/integration/raindrop"
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
token string
collectionID string
tags []string
}
func NewClient(token, collectionID, tags string) *Client {
return &Client{token: token, collectionID: collectionID, tags: strings.Split(tags, ",")}
}
// https://developer.raindrop.io/v1/raindrops/single#create-raindrop
func (c *Client) CreateRaindrop(entryURL, entryTitle string) error {
if c.token == "" {
return fmt.Errorf("raindrop: missing token")
}
var request *http.Request
requestBodyJson, err := json.Marshal(&raindrop{
Link: entryURL,
Title: entryTitle,
Collection: collection{Id: c.collectionID},
Tags: c.tags,
})
if err != nil {
return fmt.Errorf("raindrop: unable to encode request body: %v", err)
}
request, err = http.NewRequest(http.MethodPost, "https://api.raindrop.io/rest/v1/raindrop", bytes.NewReader(requestBodyJson))
if err != nil {
return fmt.Errorf("raindrop: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("Authorization", "Bearer "+c.token)
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("raindrop: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("raindrop: unable to create bookmark: status=%d", response.StatusCode)
}
return nil
}
type raindrop struct {
Link string `json:"link"`
Title string `json:"title"`
Collection collection `json:"collection,omitempty"`
Tags []string `json:"tags"`
}
type collection struct {
Id string `json:"$id"`
}

View File

@ -0,0 +1,149 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package readeck // import "miniflux.app/v2/internal/integration/readeck"
import (
"bytes"
"encoding/json"
"fmt"
"mime/multipart"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/version"
)
const defaultClientTimeout = 10 * time.Second
type Client struct {
baseURL string
apiKey string
labels string
onlyURL bool
}
func NewClient(baseURL, apiKey, labels string, onlyURL bool) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey, labels: labels, onlyURL: onlyURL}
}
func (c *Client) CreateBookmark(entryURL, entryTitle string, entryContent string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("readeck: missing base URL or API key")
}
apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/bookmarks/")
if err != nil {
return fmt.Errorf(`readeck: invalid API endpoint: %v`, err)
}
labelsSplitFn := func(c rune) bool {
return c == ',' || c == ' '
}
labelsSplit := strings.FieldsFunc(c.labels, labelsSplitFn)
var request *http.Request
if c.onlyURL {
requestBodyJson, err := json.Marshal(&readeckBookmark{
Url: entryURL,
Title: entryTitle,
Labels: labelsSplit,
})
if err != nil {
return fmt.Errorf("readeck: unable to encode request body: %v", err)
}
request, err = http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBodyJson))
if err != nil {
return fmt.Errorf("readeck: unable to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
} else {
requestBody := new(bytes.Buffer)
multipartWriter := multipart.NewWriter(requestBody)
urlPart, err := multipartWriter.CreateFormField("url")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry url): %v", err)
}
urlPart.Write([]byte(entryURL))
titlePart, err := multipartWriter.CreateFormField("title")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry title): %v", err)
}
titlePart.Write([]byte(entryTitle))
featurePart, err := multipartWriter.CreateFormField("feature_find_main")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (feature_find_main flag): %v", err)
}
featurePart.Write([]byte("false")) // false to disable readability
for _, label := range labelsSplit {
labelPart, err := multipartWriter.CreateFormField("labels")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry labels): %v", err)
}
labelPart.Write([]byte(label))
}
contentBodyHeader, err := json.Marshal(&partContentHeader{
Url: entryURL,
ContentHeader: contentHeader{ContentType: "text/html"},
})
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry content header): %v", err)
}
contentPart, err := multipartWriter.CreateFormFile("resource", "blob")
if err != nil {
return fmt.Errorf("readeck: unable to encode request body (entry content): %v", err)
}
contentPart.Write(contentBodyHeader)
contentPart.Write([]byte("\n"))
contentPart.Write([]byte(entryContent))
err = multipartWriter.Close()
if err != nil {
return fmt.Errorf("readeck: unable to encode request body: %v", err)
}
request, err = http.NewRequest(http.MethodPost, apiEndpoint, requestBody)
if err != nil {
return fmt.Errorf("readeck: unable to create request: %v", err)
}
request.Header.Set("Content-Type", multipartWriter.FormDataContentType())
}
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.Header.Set("Authorization", "Bearer "+c.apiKey)
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("readeck: unable to send request: %v", err)
}
defer response.Body.Close()
if response.StatusCode >= 400 {
return fmt.Errorf("readeck: unable to create bookmark: url=%s status=%d", apiEndpoint, response.StatusCode)
}
return nil
}
type readeckBookmark struct {
Url string `json:"url"`
Title string `json:"title"`
Labels []string `json:"labels,omitempty"`
}
type contentHeader struct {
ContentType string `json:"content-type"`
}
type partContentHeader struct {
Url string `json:"url"`
ContentHeader contentHeader `json:"headers"`
}

View File

@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package rssbridge // import "miniflux.app/v2/internal/integration/rssbridge"
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"net/url"
"strings"
"time"
)
const defaultClientTimeout = 30 * time.Second
type Bridge struct {
URL string `json:"url"`
BridgeMeta BridgeMeta `json:"bridgeMeta"`
}
type BridgeMeta struct {
Name string `json:"name"`
}
func DetectBridges(rssBridgeURL, websiteURL string) ([]*Bridge, error) {
endpointURL, err := url.Parse(rssBridgeURL)
if err != nil {
return nil, fmt.Errorf("RSS-Bridge: unable to parse bridge URL: %w", err)
}
values := endpointURL.Query()
values.Add("action", "findfeed")
values.Add("format", "atom")
values.Add("url", websiteURL)
endpointURL.RawQuery = values.Encode()
slog.Debug("Detecting RSS bridges", slog.String("url", endpointURL.String()))
request, err := http.NewRequest(http.MethodGet, endpointURL.String(), nil)
if err != nil {
return nil, fmt.Errorf("RSS-Bridge: unable to create request: %w", err)
}
httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return nil, fmt.Errorf("RSS-Bridge: unable to execute request: %w", err)
}
defer response.Body.Close()
if response.StatusCode == http.StatusNotFound {
return nil, nil
}
if response.StatusCode > 400 {
return nil, fmt.Errorf("RSS-Bridge: unexpected status code %d", response.StatusCode)
}
var bridgeResponse []*Bridge
if err := json.NewDecoder(response.Body).Decode(&bridgeResponse); err != nil {
return nil, fmt.Errorf("RSS-Bridge: unable to decode bridge response: %w", err)
}
for _, bridge := range bridgeResponse {
slog.Debug("Found RSS bridge",
slog.String("name", bridge.BridgeMeta.Name),
slog.String("url", bridge.URL),
)
if strings.HasPrefix(bridge.URL, "./") {
bridge.URL = rssBridgeURL + bridge.URL[2:]
slog.Debug("Rewrited relative RSS bridge URL",
slog.String("name", bridge.BridgeMeta.Name),
slog.String("url", bridge.URL),
)
}
}
return bridgeResponse, nil
}

View File

@ -11,7 +11,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"miniflux.app/v2/internal/urllib"
@ -74,14 +73,15 @@ func (c *Client) CreateLink(entryURL, entryTitle string) error {
}
func (c *Client) generateBearerToken() string {
header := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(`{"typ":"JWT", "alg":"HS256"}`)), "=")
payload := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(`{"iat": %d}`, time.Now().Unix()))), "=")
header := base64.RawURLEncoding.EncodeToString([]byte(`{"typ":"JWT","alg":"HS512"}`))
payload := base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf(`{"iat":%d}`, time.Now().Unix())))
data := header + "." + payload
mac := hmac.New(sha512.New, []byte(c.apiSecret))
mac.Write([]byte(header + "." + payload))
signature := strings.TrimRight(base64.URLEncoding.EncodeToString(mac.Sum(nil)), "=")
mac.Write([]byte(data))
signature := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
return header + "." + payload + "." + signature
return data + "." + signature
}
type addLinkRequest struct {

View File

@ -54,12 +54,14 @@ func (c *Client) SendSaveEntryWebhookEvent(entry *model.Entry) error {
Enclosures: entry.Enclosures,
Tags: entry.Tags,
Feed: &WebhookFeed{
ID: entry.Feed.ID,
UserID: entry.Feed.UserID,
FeedURL: entry.Feed.FeedURL,
SiteURL: entry.Feed.SiteURL,
Title: entry.Feed.Title,
CheckedAt: entry.Feed.CheckedAt,
ID: entry.Feed.ID,
UserID: entry.Feed.UserID,
CategoryID: entry.Feed.Category.ID,
Category: &WebhookCategory{ID: entry.Feed.Category.ID, Title: entry.Feed.Category.Title},
FeedURL: entry.Feed.FeedURL,
SiteURL: entry.Feed.SiteURL,
Title: entry.Feed.Title,
CheckedAt: entry.Feed.CheckedAt,
},
},
})
@ -93,16 +95,17 @@ func (c *Client) SendNewEntriesWebhookEvent(feed *model.Feed, entries model.Entr
Tags: entry.Tags,
})
}
return c.makeRequest(NewEntriesEventType, &WebhookNewEntriesEvent{
EventType: NewEntriesEventType,
Feed: &WebhookFeed{
ID: feed.ID,
UserID: feed.UserID,
FeedURL: feed.FeedURL,
SiteURL: feed.SiteURL,
Title: feed.Title,
CheckedAt: feed.CheckedAt,
ID: feed.ID,
UserID: feed.UserID,
CategoryID: feed.Category.ID,
Category: &WebhookCategory{ID: feed.Category.ID, Title: feed.Category.Title},
FeedURL: feed.FeedURL,
SiteURL: feed.SiteURL,
Title: feed.Title,
CheckedAt: feed.CheckedAt,
},
Entries: webhookEntries,
})
@ -143,12 +146,19 @@ func (c *Client) makeRequest(eventType string, payload any) error {
}
type WebhookFeed struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
CategoryID int64 `json:"category_id"`
Category *WebhookCategory `json:"category,omitempty"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at"`
}
type WebhookCategory struct {
ID int64 `json:"id"`
Title string `json:"title"`
}
type WebhookEntry struct {

View File

@ -20,7 +20,7 @@ var translationFiles embed.FS
// LoadCatalogMessages loads and parses all translations encoded in JSON.
func LoadCatalogMessages() error {
var err error
defaultCatalog = make(catalog)
defaultCatalog = make(catalog, len(AvailableLanguages()))
for language := range AvailableLanguages() {
defaultCatalog[language], err = loadTranslationFile(language)

View File

@ -53,11 +53,11 @@ func TestAllKeysHaveValue(t *testing.T) {
switch value := v.(type) {
case string:
if value == "" {
t.Errorf(`The key %q for the language %q have an empty string as value`, k, language)
t.Errorf(`The key %q for the language %q has an empty string as value`, k, language)
}
case []string:
case []any:
if len(value) == 0 {
t.Errorf(`The key %q for the language %q have an empty list as value`, k, language)
t.Errorf(`The key %q for the language %q has an empty list as value`, k, language)
}
}
}
@ -88,3 +88,20 @@ func TestMissingTranslations(t *testing.T) {
}
}
}
func TestTranslationFilePluralForms(t *testing.T) {
for language := range AvailableLanguages() {
messages, err := loadTranslationFile(language)
if err != nil {
t.Fatalf(`Unable to load translation messages for language %q`, language)
}
for k, v := range messages {
if value, ok := v.([]any); ok {
if len(value) != numberOfPluralFormsPerLanguage[language] {
t.Errorf(`The key %q for the language %q does not have the expected number of plurals, got %d instead of %d`, k, language, len(value), numberOfPluralFormsPerLanguage[language])
}
}
}
}
}

52
internal/locale/error.go Normal file
View File

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package locale // import "miniflux.app/v2/internal/locale"
import "errors"
type LocalizedErrorWrapper struct {
originalErr error
translationKey string
translationArgs []any
}
func NewLocalizedErrorWrapper(originalErr error, translationKey string, translationArgs ...any) *LocalizedErrorWrapper {
return &LocalizedErrorWrapper{
originalErr: originalErr,
translationKey: translationKey,
translationArgs: translationArgs,
}
}
func (l *LocalizedErrorWrapper) Error() error {
return l.originalErr
}
func (l *LocalizedErrorWrapper) Translate(language string) string {
if l.translationKey == "" {
return l.originalErr.Error()
}
return NewPrinter(language).Printf(l.translationKey, l.translationArgs...)
}
type LocalizedError struct {
translationKey string
translationArgs []any
}
func NewLocalizedError(translationKey string, translationArgs ...any) *LocalizedError {
return &LocalizedError{translationKey: translationKey, translationArgs: translationArgs}
}
func (v *LocalizedError) String() string {
return NewPrinter("en_US").Printf(v.translationKey, v.translationArgs...)
}
func (v *LocalizedError) Error() error {
return errors.New(v.String())
}
func (v *LocalizedError) Translate(language string) string {
return NewPrinter(language).Printf(v.translationKey, v.translationArgs...)
}

View File

@ -3,6 +3,27 @@
package locale // import "miniflux.app/v2/internal/locale"
var numberOfPluralFormsPerLanguage = map[string]int{
"en_US": 2,
"es_ES": 2,
"fr_FR": 2,
"de_DE": 2,
"pl_PL": 3,
"pt_BR": 2,
"zh_CN": 1,
"zh_TW": 1,
"nl_NL": 2,
"ru_RU": 3,
"it_IT": 2,
"ja_JP": 1,
"tr_TR": 2,
"el_EL": 2,
"fi_FI": 2,
"hi_IN": 2,
"uk_UA": 3,
"id_ID": 1,
}
// AvailableLanguages returns the list of available languages.
func AvailableLanguages() map[string]string {
return map[string]string{

View File

@ -3,69 +3,65 @@
package locale // import "miniflux.app/v2/internal/locale"
type pluralFormFunc func(n int) int
// See https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
// And http://www.unicode.org/cldr/charts/29/supplemental/language_plural_rules.html
var pluralForms = map[string]pluralFormFunc{
var pluralForms = map[string](func(n int) int){
// nplurals=2; plural=(n != 1);
"default": func(n int) int {
if n != 1 {
return 1
}
return 0
},
// nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5);
"ar_AR": func(n int) int {
if n == 0 {
switch {
case n == 0:
return 0
}
if n == 1 {
case n == 1:
return 1
}
if n == 2 {
case n == 2:
return 2
}
if n%100 >= 3 && n%100 <= 10 {
case n%100 >= 3 && n%100 <= 10:
return 3
}
if n%100 >= 11 {
case n%100 >= 11:
return 4
}
return 5
},
// nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;
"cs_CZ": func(n int) int {
if n == 1 {
switch {
case n == 1:
return 0
}
if n >= 2 && n <= 4 {
case n >= 2 && n <= 4:
return 1
}
return 2
},
// nplurals=2; plural=(n > 1);
"fr_FR": func(n int) int {
if n > 1 {
return 1
}
return 0
},
// nplurals=1; plural=0;
"id_ID": func(n int) int {
return 0
},
// nplurals=1; plural=0;
"ja_JP": func(n int) int {
return 0
},
// nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
"pl_PL": func(n int) int {
if n == 1 {
switch {
case n == 1:
return 0
}
if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) {
case n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20):
return 1
}
return 2
},
// nplurals=2; plural=(n > 1);
@ -76,23 +72,31 @@ var pluralForms = map[string]pluralFormFunc{
return 0
},
"ru_RU": pluralFormRuSrUa,
// nplurals=2; plural=(n > 1);
"tr_TR": func(n int) int {
if n > 1 {
return 1
}
return 0
},
"uk_UA": pluralFormRuSrUa,
"sr_RS": pluralFormRuSrUa,
// nplurals=1; plural=0;
"zh_CN": func(n int) int {
return 0
},
"zh_TW": func(n int) int {
return 0
},
}
// nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
func pluralFormRuSrUa(n int) int {
if n%10 == 1 && n%100 != 11 {
switch {
case n%10 == 1 && n%100 != 11:
return 0
}
if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) {
case n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20):
return 1
}
return 2
}

View File

@ -25,6 +25,20 @@ func TestPluralRules(t *testing.T) {
2: 1,
5: 2,
},
"fr_FR": {
1: 0,
2: 1,
5: 1,
},
"id_ID": {
1: 0,
5: 0,
},
"ja_JP": {
1: 0,
2: 0,
5: 0,
},
"pl_PL": {
1: 0,
2: 1,
@ -45,10 +59,24 @@ func TestPluralRules(t *testing.T) {
2: 1,
5: 2,
},
"tr_TR": {
1: 0,
2: 1,
5: 1,
},
"uk_UA": {
1: 0,
2: 1,
5: 2,
},
"zh_CN": {
1: 0,
5: 0,
},
"zh_TW": {
1: 0,
5: 0,
},
}
for rule, values := range scenarios {

View File

@ -10,6 +10,15 @@ type Printer struct {
language string
}
func (p *Printer) Print(key string) string {
if str, ok := defaultCatalog[p.language][key]; ok {
if translation, ok := str.(string); ok {
return translation
}
}
return key
}
// Printf is like fmt.Printf, but using language-specific formatting.
func (p *Printer) Printf(key string, args ...interface{}) string {
var translation string

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Sind Sie sicher?",
"confirm.question.refresh": "Möchten Sie eine erzwungene Aktualisierung durchführen?",
"confirm.yes": "ja",
@ -18,6 +19,8 @@
"action.home_screen": "Zum Startbildschirm hinzufügen",
"tooltip.keyboard_shortcuts": "Tastenkürzel: %s",
"tooltip.logged_user": "Angemeldet als %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Ungelesen",
"menu.starred": "Lesezeichen",
"menu.history": "Verlauf",
@ -32,6 +35,7 @@
"menu.about": "Über",
"menu.export": "Exportieren",
"menu.import": "Importieren",
"menu.search": "Suche",
"menu.create_category": "Kategorie anlegen",
"menu.mark_page_as_read": "Diese Seite als gelesen markieren",
"menu.mark_all_as_read": "Alle als gelesen markieren",
@ -50,6 +54,7 @@
"menu.shared_entries": "Geteilte Artikel",
"search.label": "Suche",
"search.placeholder": "Suche...",
"search.submit": "Search",
"pagination.next": "Nächste",
"pagination.previous": "Vorherige",
"entry.status.unread": "Ungelesen",
@ -81,11 +86,27 @@
"entry.estimated_reading_time": [
"%d Minute zu lesen",
"%d Minuten zu lesen"
],
],
"entry.tags.label": "Stichworte:",
"page.shared_entries.title": "Geteilte Artikel",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Ungelesen",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Lesezeichen",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Kategorien",
"page.categories.no_feed": "Kein Abonnement.",
"page.categories.entries": "Artikel",
@ -94,20 +115,28 @@
"Es gibt %d Abonnement.",
"Es gibt %d Abonnements."
],
"page.categories.unread_counter": "Anzahl der ungelesenen Artikel",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "Neue Kategorie",
"page.new_user.title": "Neuer Benutzer",
"page.edit_category.title": "Kategorie bearbeiten: %s",
"page.edit_user.title": "Benutzer bearbeiten: %s",
"page.feeds.title": "Abonnements",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Letzte Aktualisierung:",
"page.feeds.unread_counter": "Anzahl der ungelesenen Artikel",
"page.feeds.next_check": "Nächste Aktualisierung:",
"page.feeds.read_counter": "Anzahl der gelesenen Artikel",
"page.feeds.error_count": [
"%d Fehler",
"%d Fehler"
],
"page.history.title": "Verlauf",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Importieren",
"page.search.title": "Suchergebnisse",
"page.about.title": "Über",
@ -117,12 +146,12 @@
"page.about.author": "Autor:",
"page.about.license": "Lizenz:",
"page.about.global_config_options": "Globale Konfigurationsoptionen",
"page.about.postgres_version": "Postgres Version:",
"page.about.go_version": "Go Version:",
"page.about.postgres_version": "Postgres-Version:",
"page.about.go_version": "Go-Version:",
"page.add_feed.title": "Neues Abonnement",
"page.add_feed.no_category": "Es ist keine Kategorie vorhanden. Wenigstens eine Kategorie muss angelegt sein.",
"page.add_feed.label.url": "URL",
"page.add_feed.submit": "Abonnement suchen",
"page.add_feed.submit": "Abonnement finden",
"page.add_feed.legend.advanced_options": "Erweiterte Optionen",
"page.add_feed.choose_feed": "Abonnement auswählen",
"page.edit_feed.title": "Abonnement bearbeiten: %s",
@ -131,7 +160,7 @@
"page.edit_feed.etag_header": "ETag-Kopfzeile:",
"page.edit_feed.no_header": "Nicht verfügbar",
"page.edit_feed.last_parsing_error": "Letzter Analysefehler",
"page.entry.attachments": "Anlagen",
"page.entry.attachments": "Anhänge",
"page.keyboard_shortcuts.title": "Tastenkürzel",
"page.keyboard_shortcuts.subtitle.sections": "Navigation zwischen den Menüpunkten",
"page.keyboard_shortcuts.subtitle.items": "Navigation zwischen den Artikeln",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Zum Abonnement gehen",
"page.keyboard_shortcuts.go_to_previous_page": "Zur vorherigen Seite gehen",
"page.keyboard_shortcuts.go_to_next_page": "Zur nächsten Seite gehen",
"page.keyboard_shortcuts.go_to_bottom_item": "Gehen Sie zum untersten Element",
"page.keyboard_shortcuts.go_to_top_item": "Zum obersten Artikel gehen",
"page.keyboard_shortcuts.open_item": "Gewählten Artikel öffnen",
"page.keyboard_shortcuts.open_original": "Original-Artikel öffnen",
"page.keyboard_shortcuts.open_original_same_window": "Öffne den Original-Link in der aktuellen Registerkarte",
@ -161,10 +192,10 @@
"page.keyboard_shortcuts.download_content": "Vollständigen Inhalt herunterladen",
"page.keyboard_shortcuts.toggle_bookmark_status": "Lesezeichen hinzufügen/entfernen",
"page.keyboard_shortcuts.save_article": "Artikel speichern",
"page.keyboard_shortcuts.scroll_item_to_top": "Artikel nach oben blättern",
"page.keyboard_shortcuts.scroll_item_to_top": "Artikel an den Anfang blättern",
"page.keyboard_shortcuts.remove_feed": "Dieses Abonnement entfernen",
"page.keyboard_shortcuts.go_to_search": "Fokus auf das Suchformular setzen",
"page.keyboard_shortcuts.toggle_entry_attachments": "Artikel Anhänge öffnen/schließen",
"page.keyboard_shortcuts.toggle_entry_attachments": "Artikelanhänge öffnen/schließen",
"page.keyboard_shortcuts.close_modal": "Liste der Tastenkürzel schließen",
"page.users.title": "Benutzer",
"page.users.username": "Benutzername",
@ -175,26 +206,39 @@
"page.users.last_login": "Letzte Anmeldung",
"page.users.is_admin": "Administrator",
"page.settings.title": "Einstellungen",
"page.settings.link_google_account": "Google Konto verknüpfen",
"page.settings.unlink_google_account": "Google Konto Verknüpfung entfernen",
"page.settings.link_oidc_account": "OpenID Connect Konto verknüpfen",
"page.settings.unlink_oidc_account": "OpenID Connect Konto Verknüpfung entfernen",
"page.settings.link_google_account": "Google-Konto verknüpfen",
"page.settings.unlink_google_account": "Verknüpfung mit Google-Konto entfernen",
"page.settings.link_oidc_account": "OpenID-Connect-Konto verknüpfen",
"page.settings.unlink_oidc_account": "Verknüpfung mit OpenID-Connect-Konto entfernen",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Aktionen",
"page.settings.webauthn.passkey_name": "Name des Passkeys",
"page.settings.webauthn.added_on": "Hinzugefügt am",
"page.settings.webauthn.last_seen_on": "Zuletzt genutzt",
"page.settings.webauthn.register": "Hauptschlüssel registrieren",
"page.settings.webauthn.register.error": "Hauptschlüssel kann nicht registriert werden",
"page.settings.webauthn.delete": [
"Entfernen Sie %d Hauptschlüssel",
"%d Hauptschlüssel entfernen"
],
"page.login.title": "Anmeldung",
"page.login.google_signin": "Anmeldung mit Google",
"page.login.oidc_signin": "Anmeldung mit OpenID Connect",
"page.login.webauthn_login": "Melden Sie sich mit dem Passkey an",
"page.login.webauthn_login.error": "Anmeldung mit Passkey nicht möglich",
"page.integrations.title": "Dienste",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Endpunkt",
"page.integration.miniflux_api": "Miniflux-API",
"page.integration.miniflux_api_endpoint": "API-Endpunkt",
"page.integration.miniflux_api_username": "Benutzername",
"page.integration.miniflux_api_password": "Passwort",
"page.integration.miniflux_api_password_value": "Ihr Konto Passwort",
"page.integration.miniflux_api_password_value": "Ihr Konto-Passwort",
"page.integration.bookmarklet": "Bookmarklet",
"page.integration.bookmarklet.name": "Mit Miniflux abonnieren",
"page.integration.bookmarklet.instructions": "Ziehen Sie diesen Link in Ihre Lesezeichen.",
"page.integration.bookmarklet.help": "Dieser spezielle Link ermöglicht es, eine Webseite direkt über ein Lesezeichen im Browser zu abonnieren.",
"page.sessions.title": "Sitzungen",
"page.sessions.table.date": "Datum",
"page.sessions.table.ip": "IP Addresse",
"page.sessions.table.ip": "IP-Addresse",
"page.sessions.table.user_agent": "Benutzeragent",
"page.sessions.table.actions": "Aktionen",
"page.sessions.table.current_session": "Aktuelle Sitzung",
@ -209,10 +253,12 @@
"page.offline.title": "Offline-Modus",
"page.offline.message": "Du bist offline",
"page.offline.refresh_page": "Versuchen Sie, die Seite zu aktualisieren",
"page.webauthn_rename.title": "Passkey umbenennen",
"alert.no_shared_entry": "Es existieren derzeit keine geteilten Artikel.",
"alert.no_bookmark": "Es existiert derzeit kein Lesezeichen.",
"alert.no_category": "Es ist keine Kategorie vorhanden.",
"alert.no_category_entry": "Es befindet sich kein Artikel in dieser Kategorie.",
"alert.no_tag_entry": "Es gibt keine Artikel, die diesem Tag entsprechen.",
"alert.no_feed_entry": "Es existiert kein Artikel für dieses Abonnement.",
"alert.no_feed": "Es sind keine Abonnements vorhanden.",
"alert.no_feed_in_category": "Für diese Kategorie gibt es kein Abonnement.",
@ -239,6 +285,13 @@
"error.unable_to_update_user": "Dieser Benutzer konnte nicht aktualisiert werden.",
"error.unable_to_update_feed": "Dieses Abonnement konnte nicht aktualisiert werden.",
"error.subscription_not_found": "Es wurden keine Abonnements gefunden.",
"error.invalid_theme": "Ungültiges Thema.",
"error.invalid_language": "Ungültige Sprache.",
"error.invalid_timezone": "Ungültige Zeitzone.",
"error.invalid_entry_direction": "Ungültige Sortierreihenfolge.",
"error.invalid_display_mode": "Progressive Web App (PWA) Anzeigemodus",
"error.invalid_gesture_nav": "Ungültige Gestennavigation.",
"error.invalid_default_home_page": "Ungültige Standard-Startseite!",
"error.empty_file": "Diese Datei ist leer.",
"error.bad_credentials": "Benutzername oder Passwort ungültig.",
"error.fields_mandatory": "Alle Felder sind obligatorisch.",
@ -261,77 +314,72 @@
"error.user_mandatory_fields": "Der Benutzername ist obligatorisch.",
"error.api_key_already_exists": "Dieser API-Schlüssel ist bereits vorhanden.",
"error.unable_to_create_api_key": "Dieser API-Schlüssel kann nicht erstellt werden.",
"error.invalid_theme": "Ungültiges Thema.",
"error.invalid_language": "Ungültige Sprache.",
"error.invalid_timezone": "Ungültige Zeitzone.",
"error.invalid_entry_direction": "Ungültige Sortierreihenfolge.",
"error.invalid_display_mode": "Progressive Web App (PWA) Anzeigemodus",
"error.invalid_gesture_nav": "Ungültige Gestennavigation.",
"error.invalid_default_home_page": "Ungültige Standard-Startseite!",
"form.feed.label.title": "Titel",
"form.feed.label.site_url": "Webseite-URL",
"form.feed.label.feed_url": "Abonnement-URL",
"form.feed.label.site_url": "URL der Webseite",
"form.feed.label.feed_url": "URL des Abonnements",
"form.feed.label.description": "Beschreibung",
"form.feed.label.category": "Kategorie",
"form.feed.label.crawler": "Inhalt herunterladen",
"form.feed.label.crawler": "Originalinhalt herunterladen",
"form.feed.label.feed_username": "Benutzername des Abonnements",
"form.feed.label.feed_password": "Passwort des Abonnements",
"form.feed.label.user_agent": "Standardbenutzeragenten überschreiben",
"form.feed.label.cookie": "Cookies setzen",
"form.feed.label.scraper_rules": "Extraktionsregeln",
"form.feed.label.rewrite_rules": "Umschreiberegeln",
"form.feed.label.apprise_service_urls": "Kommaseparierte Liste der Apprise Service-URLs",
"form.feed.label.blocklist_rules": "Blockierregeln",
"form.feed.label.keeplist_rules": "Erlaubnisregeln",
"form.feed.label.urlrewrite_rules": "Umschreibregeln für URL",
"form.feed.label.apprise_service_urls": "Kommaseparierte Liste der Apprise service URLs",
"form.feed.label.ignore_http_cache": "Ignoriere HTTP-cache",
"form.feed.label.ignore_http_cache": "Ignoriere HTTP-Cache",
"form.feed.label.allow_self_signed_certificates": "Erlaube selbstsignierte oder ungültige Zertifikate",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Über Proxy abrufen",
"form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren",
"form.feed.label.no_media_player": "No media player (audio/video)",
"form.feed.label.no_media_player": "Kein Media-Player (Audio/Video)",
"form.feed.label.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden",
"form.feed.fieldset.general": "General",
"form.feed.fieldset.rules": "Rules",
"form.feed.fieldset.network_settings": "Network Settings",
"form.feed.fieldset.integration": "Third-Party Services",
"form.feed.fieldset.general": "Allgemein",
"form.feed.fieldset.rules": "Regeln",
"form.feed.fieldset.network_settings": "Netzwerkeinstellungen",
"form.feed.fieldset.integration": "Drittanbieter-Dienste",
"form.category.label.title": "Titel",
"form.category.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden",
"form.user.label.username": "Benutzername",
"form.user.label.password": "Passwort",
"form.user.label.confirmation": "Passwort Bestätigung",
"form.user.label.confirmation": "Passwortbestätigung",
"form.user.label.admin": "Administrator",
"form.prefs.label.language": "Sprache",
"form.prefs.label.timezone": "Zeitzone",
"form.prefs.label.theme": "Thema",
"form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.label.entry_sorting": "Sortierung der Einträge",
"form.prefs.label.entries_per_page": "Einträge pro Seite",
"form.prefs.label.default_reading_speed": "Lesegeschwindigkeit für andere Sprachen (Wörter pro Minute)",
"form.prefs.label.cjk_reading_speed": "Lesegeschwindigkeit für Chinesisch, Koreanisch und Japanisch (Zeichen pro Minute)",
"form.prefs.label.display_mode": "Anzeigemodus der Web-App (muss neu installiert werden)",
"form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.label.display_mode": "Anzeigemodus der progressiven Web-Anwendung (PWA)",
"form.prefs.select.older_first": "Ältere Einträge zuerst",
"form.prefs.select.recent_first": "Neue Einträge zuerst",
"form.prefs.select.fullscreen": "Vollbildschirm",
"form.prefs.select.standalone": "Eigenständige",
"form.prefs.select.minimal_ui": "Minimal",
"form.prefs.select.browser": "Browser",
"form.prefs.select.publish_time": "Eintrag veröffentlichte Zeit",
"form.prefs.select.created_time": "Eintrag erstellt Zeit",
"form.prefs.select.publish_time": "Artikel veröffentlichte am",
"form.prefs.select.created_time": "Artikel erstellt am",
"form.prefs.select.alphabetical": "Alphabetisch",
"form.prefs.select.unread_count": "Ungelesen zählen",
"form.prefs.select.none": "Keiner",
"form.prefs.select.unread_count": "Ungelesen",
"form.prefs.select.none": "Keine",
"form.prefs.select.tap": "Doppeltippen",
"form.prefs.select.swipe": "Wischen",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
"form.prefs.label.entry_swipe": "Aktivieren Sie das Streichen von Einträgen auf Touchscreens",
"form.prefs.label.entry_swipe": "Aktivieren Sie das Wischen von Einträgen auf Touchscreens",
"form.prefs.label.gesture_nav": "Geste zum Navigieren zwischen Einträgen",
"form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen",
"form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
"form.prefs.label.entry_order": "Eintrag Sortierspalte",
"form.prefs.label.default_home_page": "Standard Startseite",
"form.prefs.label.categories_sorting_order": "Kategorien sortieren",
"form.prefs.label.entry_order": "Artikel-Sortierspalte",
"form.prefs.label.default_home_page": "Standard-Startseite",
"form.prefs.label.categories_sorting_order": "Kategorie-Sortierung",
"form.prefs.label.mark_read_on_view": "Einträge automatisch als gelesen markieren, wenn sie angezeigt werden",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
"form.prefs.fieldset.application_settings": "Anwendungseinstellungen",
"form.prefs.fieldset.authentication_settings": "Authentifizierungseinstellungen",
"form.prefs.fieldset.reader_settings": "Reader-Einstellungen",
"form.import.label.file": "OPML Datei",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Fever API aktivieren",
@ -341,68 +389,91 @@
"form.integration.googlereader_activate": "Google Reader API aktivieren",
"form.integration.googlereader_username": "Google Reader Benutzername",
"form.integration.googlereader_password": "Google Reader Passwort",
"form.integration.googlereader_endpoint": "Google Reader API Endpunkt:",
"form.integration.pinboard_activate": "Artikel in Pinboard speichern",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.googlereader_endpoint": "Google Reader API-Endpunkt:",
"form.integration.pinboard_activate": "Einträge in Pinboard speichern",
"form.integration.pinboard_token": "Pinboard API-Token",
"form.integration.pinboard_tags": "Pinboard Tags",
"form.integration.pinboard_bookmark": "Lesezeichen als ungelesen markieren",
"form.integration.instapaper_activate": "Artikel in Instapaper speichern",
"form.integration.instapaper_activate": "Einträge in Instapaper speichern",
"form.integration.instapaper_username": "Instapaper Benutzername",
"form.integration.instapaper_password": "Instapaper Passwort",
"form.integration.pocket_activate": "Artikel in Pocket speichern",
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
"form.integration.pocket_access_token": "Pocket Access Token",
"form.integration.pocket_activate": "Einträge in Pocket speichern",
"form.integration.pocket_consumer_key": "Pocket Verbraucher-Schlüssel",
"form.integration.pocket_access_token": "Pocket Zugangs-Token",
"form.integration.pocket_connect_link": "Verbinden Sie Ihr Pocket Konto",
"form.integration.wallabag_activate": "Artikel in Wallabag speichern",
"form.integration.wallabag_activate": "Einträge in Wallabag speichern",
"form.integration.wallabag_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)",
"form.integration.wallabag_endpoint": "Wallabag URL",
"form.integration.wallabag_client_id": "Wallabag Client-ID",
"form.integration.wallabag_client_secret": "Wallabag Client-Secret",
"form.integration.wallabag_client_secret": "Wallabag Client-Geheimnis",
"form.integration.wallabag_username": "Wallabag Benutzername",
"form.integration.wallabag_password": "Wallabag Passwort",
"form.integration.notion_activate": "Save entries to Notion",
"form.integration.notion_activate": "Einträge in Notion speichern",
"form.integration.notion_page_id": "Notion Page ID",
"form.integration.notion_token": "Notion Secret Token",
"form.integration.notion_token": "Notion Geheimnis-Token",
"form.integration.apprise_activate": "Push entries to Apprise",
"form.integration.apprise_url": "Apprise API URL",
"form.integration.apprise_services_url": "Kommaseparierte Liste der Apprise service URLs",
"form.integration.apprise_services_url": "Kommaseparierte Liste von Apprise service URLs",
"form.integration.nunux_keeper_activate": "Artikel in Nunux Keeper speichern",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API-Endpunkt",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-Schlüssel",
"form.integration.espial_activate": "Artikel in Espial speichern",
"form.integration.omnivore_activate": "Einträge in Omnivore speichern",
"form.integration.omnivore_url": "Omnivore API-Endpunkt",
"form.integration.omnivore_api_key": "Omnivore API-Schlüssel",
"form.integration.espial_activate": "Einträge in Espial",
"form.integration.espial_endpoint": "Espial API-Endpunkt",
"form.integration.espial_api_key": "Espial API-Schlüssel",
"form.integration.espial_tags": "Espial tags",
"form.integration.readwise_activate": "Save entries to Readwise Reader",
"form.integration.readwise_api_key": "Readwise Reader Access Token",
"form.integration.readwise_api_key_link": "Get your Readwise Access Token",
"form.integration.telegram_bot_activate": "Pushen Sie neue Artikel in den Telegram-Chat",
"form.integration.telegram_bot_token": "Bot token",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.telegram_topic_id": "Topic ID",
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.espial_tags": "Espial Tags",
"form.integration.readwise_activate": "Einträge in Readwise Reader speichern",
"form.integration.readwise_api_key": "Readwise Reader Zugangs-Token",
"form.integration.readwise_api_key_link": "Erhalten Sie Ihren Readwise Zugangs-Token",
"form.integration.telegram_bot_activate": "Schicken Sie neue Artikel in den Telegram-Chat",
"form.integration.telegram_bot_token": "Bot-Token",
"form.integration.telegram_chat_id": "Chat-ID",
"form.integration.telegram_topic_id": "Thema-ID",
"form.integration.telegram_bot_disable_web_page_preview": "Webseiten-Vorschau deaktivieren",
"form.integration.telegram_bot_disable_notification": "Benachrichtigungen deaktivieren",
"form.integration.telegram_bot_disable_buttons": "Schaltfächen deaktivieren",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Artikel in Linkding speichern",
"form.integration.linkding_endpoint": "Linkding API-Endpunkt",
"form.integration.linkding_api_key": "Linkding API-Schlüssel",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Lesezeichen als ungelesen markieren",
"form.integration.matrix_bot_activate": "Neue Artikel in die Matrix übertragen",
"form.integration.linkwarden_activate": "Artikel in Linkwarden speichern",
"form.integration.linkwarden_endpoint": "Linkwarden API-Endpunkt",
"form.integration.linkwarden_api_key": "Linkwarden API-Schlüssel",
"form.integration.matrix_bot_activate": "Neue Artikel in Matrix übertragen",
"form.integration.matrix_bot_user": "Benutzername für Matrix",
"form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer",
"form.integration.matrix_bot_url": "URL des Matrix-Servers",
"form.integration.matrix_bot_chat_id": "ID des Matrix-Raums",
"form.integration.shiori_activate": "Artikel in Shiori",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Artikel in Readeck speichern",
"form.integration.readeck_endpoint": "Readeck API-Endpunkt",
"form.integration.readeck_api_key": "Readeck API-Schlüssel",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)",
"form.integration.shiori_activate": "Artikel in Shiori speichern",
"form.integration.shiori_endpoint": "Shiori API-Endpunkt",
"form.integration.shiori_username": "Shiori Benutzername",
"form.integration.shiori_password": "Shiori Passwort",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_activate": "Artikel in Shaarli speichern",
"form.integration.shaarli_endpoint": "Shaarli URL",
"form.integration.shaarli_api_secret": "Shaarli API Secret",
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.shaarli_api_secret": "Shaarli API Geheimnis",
"form.integration.webhook_activate": "Webhook aktivieren",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.webhook_secret": "Webhook Geheimnis",
"form.integration.rssbridge_activate": "Beim Hinzufügen von Abonnements RSS-Bridge prüfen.",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "API-Schlüsselbezeichnung",
"form.submit.loading": "Lade...",
"form.submit.saving": "Speichern...",
@ -433,23 +504,44 @@
"vor %d Jahr",
"vor %d Jahren"
],
"This feed already exists (%s)": "Diese Abonnement existiert bereits (%s)",
"Unable to fetch feed (Status Code = %d)": "Abonnement konnte nicht abgerufen werden (code=%d)",
"Unable to open this link: %v": "Dieser Link konnte nicht geöffnet werden: %v",
"Unable to analyze this page: %v": "Diese Seite konnte nicht analysiert werden: %v",
"Unable to execute request: %v": "Diese Anfrage konnte nicht ausgeführt werden: %v",
"Unable to parse OPML file: %q": "OPML Datei konnte nicht gelesen werden: %q",
"Unable to parse RSS feed: %q": "RSS Abonnement konnte nicht gelesen werden: %q",
"Unable to parse Atom feed: %q": "Atom Abonnement konnte nicht gelesen werden: %q",
"Unable to parse JSON feed: %q": "JSON Abonnement konnte nicht gelesen werden: %q",
"Unable to parse RDF feed: %q": "RDF Abonnement konnte nicht gelesen werden: %q",
"Unable to normalize encoding: %q": "Zeichenkodierung konnte nicht normalisiert werden: %q",
"This feed is empty": "Dieses Abonnement ist leer",
"This web page is empty": "Diese Webseite ist leer",
"Invalid SSL certificate (original error: %q)": "Ungültiges SSL-Zertifikat (ursprünglicher Fehler: %q)",
"This website is unreachable (original error: %q)": "Diese Webseite ist nicht erreichbar (ursprünglicher Fehler: %q)",
"Website unreachable, the request timed out after %d seconds": "Webseite nicht erreichbar, die Anfrage endete nach %d Sekunden",
"You are not authorized to access this resource (invalid username/password)": "Sie sind nicht berechtigt, auf diese Ressource zuzugreifen (Benutzername/Passwort ungültig)",
"Unable to fetch this resource (Status Code = %d)": "Ressource konnte nicht abgerufen werden (code=%d)",
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Ressource nicht gefunden (404), dieses Abonnement existiert nicht mehr, überprüfen Sie die Abonnement-URL"
"alert.too_many_feeds_refresh": [
"Sie haben zu viele Aktualisierungen ausgelöst. Bitte warten Sie %d Minute, bevor Sie es erneut versuchen.",
"Sie haben zu viele Aktualisierungen ausgelöst. Bitte warten Sie %d Minuten, bevor Sie es erneut versuchen."
],
"alert.background_feed_refresh": "Alle Abonnements werden derzeit im Hintergrund aktualisiert. Sie können Miniflux weiterhin benutzen, während dieser Prozess ausgeführt wird.",
"error.http_response_too_large": "Die HTTP-Antwort ist zu groß. Sie könnten die Grenze für die Größe der HTTP-Antwort in den globalen Einstellungen erhöhen (benötigt einen Neustart des Servers)",
"error.http_body_read": "Der HTTP-Inhalt kann nicht gelesen werden: %v",
"error.http_empty_response_body": "Der Inhalt der HTTP-Antwort ist leer.",
"error.http_empty_response": "Die HTTP-Antwort ist leer. Vielleicht versucht die Webseite, sich vor Bots zu schützen?",
"error.tls_error": "TLS-Fehler: %q. Wenn Sie mögen, können Sie versuchen die TLS-Verifizierung in den Einstellungen des Abonnements zu deaktivieren.",
"error.network_operation": "Miniflux kann die Webseite aufgrund eines Netzwerk-Fehlers nicht erreichen: %v",
"error.network_timeout": "Die Webseite ist zu langsam und die Anfrage ist abgelaufen: %v.",
"error.http_client_error": "HTTP-Client-Fehler: %v.",
"error.http_not_authorized": "Der Zugriff auf diese Website ist nicht erlaubt. Möglicherweise sind der Benutzername oder das Passwort falsch.",
"error.http_too_many_requests": "Miniflux hat zu viele Anfragen an diese Webseite gestellt. Bitte versuchen Sie es später erneut oder ändern Sie die Konfiguration der Anwendung.",
"error.http_forbidden": "Der Zugriff auf diese Webseite ist verboten. Vielleicht versucht die Webseite, sich vor Bots zu schützen?",
"error.http_resource_not_found": "Die gewünschte Quelle wurde nicht gefunden. Bitte stellen Sie sicher, dass die URL korrekt ist.",
"error.http_internal_server_error": "Die Webseite steht durch einen Server-Fehler derzeit nicht zur Verfügung. Versuchen Sie es bitte später erneut.",
"error.http_bad_gateway": "Die Webseite ist aufgrund eines Bad-Gateway-Fehlers derzeit nicht verfügbar. Das Problem liegt nicht bei Miniflux. Bitte versuchen Sie es später erneut.",
"error.http_service_unavailable": "Die Webseite ist aufgrund eines Internal-Server-Fehlers derzeit nicht verfügbar. Das Problem liegt nicht bei Miniflux. Bitte versuchen Sie es später erneut.",
"error.http_gateway_timeout": "Die Webseite ist aufgrund eines Gateway-Timeout-Fehlers derzeit nicht verfügbar. Das Problem liegt nicht bei Miniflux. Bitte versuchen Sie es später erneut.",
"error.http_unexpected_status_code": "Die Webseite ist aufgrund eines eines unerwarteten HTTP-Fehlers derzeit nicht verfügbar: %d. Das Problem liegt nicht bei Miniflux. Bitte versuchen Sie es später erneut.",
"error.database_error": "Datenbank-Fehler: %v.",
"error.category_not_found": "Diese Kategorie existiert nicht oder gehört nicht zu diesem Benutzer.",
"error.duplicated_feed": "Dieses Abonnement existiert bereits.",
"error.unable_to_parse_feed": "Dieses Abonnement kann nicht gelesen werden: %v.",
"error.feed_not_found": "Dieses Abonnement existiert nicht oder gehört nicht zu diesem Benutzer.",
"error.unable_to_detect_rssbridge": "Abonnement kann nicht durch RSS-Bridge erkannt werden: %v.",
"error.feed_format_not_detected": "Das Format des Abonnements kann nicht erkannt werden: %v.",
"form.prefs.label.media_playback_rate": "Wiedergabegeschwindigkeit von Audio/Video",
"error.settings_media_playback_rate_range": "Die Wiedergabegeschwindigkeit liegt außerhalb des Bereichs",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Είστε σίγουροι;",
"confirm.question.refresh": "Θέλετε να επιτελέσετε μια υποχρεωτική ανανέωση;",
"confirm.yes": "ναι",
@ -18,6 +19,8 @@
"action.home_screen": "Προσθήκη στην αρχική οθόνη",
"tooltip.keyboard_shortcuts": "Συντόμευση πληκτρολογίου: % s",
"tooltip.logged_user": "Συνδεδεμένος/η ως %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Μη αναγνωσμένα",
"menu.starred": "Αγαπημένα",
"menu.history": "Ιστορικό",
@ -32,6 +35,7 @@
"menu.about": "Περί",
"menu.export": "Εξαγωγή",
"menu.import": "Εισαγωγή",
"menu.search": "Αναζήτηση",
"menu.create_category": "Δημιουργήστε μια κατηγορία",
"menu.mark_page_as_read": "Σημείωση αυτής της σελίδας ως αναγνωσμένη",
"menu.mark_all_as_read": "Σημείωση όλων ως αναγνωσμένα",
@ -50,6 +54,7 @@
"menu.shared_entries": "Κοινόχρηστες καταχωρήσεις",
"search.label": "Αναζήτηση",
"search.placeholder": "Αναζήτηση...",
"search.submit": "Search",
"pagination.next": "Επόμενη",
"pagination.previous": "Προηγούμενη",
"entry.status.unread": "Μη αναγνωσμένο",
@ -84,8 +89,24 @@
],
"entry.tags.label": "Ετικέτες:",
"page.shared_entries.title": "Κοινόχρηστες Καταχωρήσεις",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Μη αναγνωσμένα",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Αγαπημένo",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Κατηγορίες",
"page.categories.no_feed": "Καμία ροή.",
"page.categories.entries": "Άρθρα",
@ -94,20 +115,28 @@
"Υπάρχει μία %d ροή.",
"Υπάρχουν %d ροές."
],
"page.categories.unread_counter": "Αριθμός μη αναγνωσμένων καταχωρήσεων",
"page.new_category.title": "Νέα Κατηγορία",
"page.new_user.title": "Νέος Χρήστης",
"page.edit_category.title": "Επεξεργασία κατηγορίας: % s",
"page.edit_user.title": "Επεξεργασία χρήστη: % s",
"page.feeds.title": "Ροές",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Τελευταίος έλεγχος:",
"page.feeds.unread_counter": "Αριθμός μη αναγνωσμένων καταχωρήσεων",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Αριθμός αναγνωσμένων καταχωρήσεων",
"page.feeds.error_count": [
"%d σφάλμα",
"%d σφάλματα"
],
"page.history.title": "Ιστορικό",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.categories_count": [
"%d category",
"%d categories"
],
"page.import.title": "Εισαγωγή",
"page.search.title": "Αποτελέσματα Αναζήτησης",
"page.about.title": "Περί",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Πηγαίνετε στη ροή",
"page.keyboard_shortcuts.go_to_previous_page": "Μετάβαση στην προηγούμενη σελίδα",
"page.keyboard_shortcuts.go_to_next_page": "Μετάβαση στην επόμενη σελίδα",
"page.keyboard_shortcuts.go_to_bottom_item": "Μετάβαση στο κάτω στοιχείο",
"page.keyboard_shortcuts.go_to_top_item": "Μετάβαση στο επάνω στοιχείο",
"page.keyboard_shortcuts.open_item": "Άνοιγμα επιλεγμένου στοιχείου",
"page.keyboard_shortcuts.open_original": "Άνοιγμα αρχικού συνδέσμου",
"page.keyboard_shortcuts.open_original_same_window": "Άνοιγμα αρχικού συνδέσμου στην τρέχουσα καρτέλα",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Αποσύνδεση του λογαριασμού μου Google",
"page.settings.link_oidc_account": "Σύνδεση του λογαριασμού μου OpenID Connect",
"page.settings.unlink_oidc_account": "Αποσύνδεση του λογαριασμού μου OpenID Connect",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Εγγραφή κωδικού πρόσβασης",
"page.settings.webauthn.register.error": "Δεν είναι δυνατή η εγγραφή του κωδικού πρόσβασης",
"page.settings.webauthn.delete": [
"Αφαιρέστε %d κωδικό πρόσβασης",
"Καταργήστε %d κωδικούς πρόσβασης"
],
"page.login.title": "Είσοδος",
"page.login.google_signin": "Συνδεθείτε με τo Google",
"page.login.oidc_signin": "Συνδεθείτε με το OpenID Connect",
"page.login.webauthn_login": "Είσοδος με κωδικό πρόσβασης",
"page.login.webauthn_login.error": "Δεν είναι δυνατή η σύνδεση με κωδικό πρόσβασης",
"page.integrations.title": "Ενσωμάτωση",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "Τελικό σημείο API",
@ -209,10 +253,12 @@
"page.offline.title": "Λειτουργία Εκτός Σύνδεσης",
"page.offline.message": "Είστε εκτός σύνδεσης",
"page.offline.refresh_page": "Προσπαθήστε να ανανεώσετε τη σελίδα",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Δεν υπάρχει κοινόχρηστη καταχώρηση.",
"alert.no_bookmark": "Δεν υπάρχει σελιδοδείκτης αυτή τη στιγμή.",
"alert.no_category": "Δεν υπάρχει κατηγορία.",
"alert.no_category_entry": "Δεν υπάρχουν άρθρα σε αυτήν την κατηγορία.",
"alert.no_tag_entry": "Δεν υπάρχουν αντικείμενα που να ταιριάζουν με αυτή την ετικέτα.",
"alert.no_feed_entry": "Δεν υπάρχουν άρθρα για αυτήν τη ροή.",
"alert.no_feed": "Δεν έχετε συνδρομές.",
"alert.no_feed_in_category": "Δεν υπάρχει συνδρομή για αυτήν την κατηγορία.",
@ -273,6 +319,7 @@
"form.feed.label.title": "Τίτλος",
"form.feed.label.site_url": "Διεύθυνση URL ιστότοπου",
"form.feed.label.feed_url": "Διεύθυνση URL ροής",
"form.feed.label.description": "Περιγραφή",
"form.feed.label.category": "Κατηγορία",
"form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
"form.feed.label.feed_username": "Όνομα Χρήστη ροής",
@ -285,6 +332,7 @@
"form.feed.label.keeplist_rules": "Κρατήστε Κανόνες",
"form.feed.label.ignore_http_cache": "Αγνοήστε την προσωρινή μνήμη HTTP",
"form.feed.label.allow_self_signed_certificates": "Να επιτρέπονται αυτο-υπογεγραμμένα ή μη έγκυρα πιστοποιητικά",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Λήψη μέσω διακομιστή μεσολάβησης",
"form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Αποθήκευση άρθρων στο Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Τελικό σημείο Nunux Keeper API",
"form.integration.nunux_keeper_api_key": "Κλειδί API Nunux Keeper",
"form.integration.omnivore_activate": "Αποθήκευση άρθρων στο Omnivore",
"form.integration.omnivore_url": "Τελικό σημείο Omnivore API",
"form.integration.omnivore_api_key": "Κλειδί API Omnivore",
"form.integration.espial_activate": "Αποθήκευση άρθρων στο Espial",
"form.integration.espial_endpoint": "Τελικό σημείο Espial API",
"form.integration.espial_api_key": "Κλειδί API Espial",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Αποθήκευση άρθρων στο Linkding",
"form.integration.linkding_endpoint": "Τελικό σημείο Linkding API",
"form.integration.linkding_api_key": "Κλειδί API Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Σημείωση του σελιδοδείκτη ως μη αναγνωσμένου",
"form.integration.linkwarden_activate": "Αποθήκευση άρθρων στο Linkwarden",
"form.integration.linkwarden_endpoint": "Τελικό σημείο Linkwarden API",
"form.integration.linkwarden_api_key": "Κλειδί API Linkwarden",
"form.integration.matrix_bot_activate": "Μεταφορά νέων άρθρων στο Matrix",
"form.integration.matrix_bot_user": "Όνομα χρήστη για το Matrix",
"form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix",
"form.integration.matrix_bot_url": "URL διακομιστή Matrix",
"form.integration.matrix_bot_chat_id": "Αναγνωριστικό της αίθουσας Matrix",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Αποθήκευση άρθρων στο Readeck",
"form.integration.readeck_endpoint": "Τελικό σημείο Readeck API",
"form.integration.readeck_api_key": "Κλειδί API Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Αποστολή μόνο URL (αντί για πλήρες περιεχόμενο)",
"form.integration.shiori_activate": "Αποθήκευση άρθρων στο Shiori",
"form.integration.shiori_endpoint": "Τελικό σημείο Shiori",
"form.integration.shiori_username": "Όνομα Χρήστη Shiori",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "Ετικέτα κλειδιού API",
"form.submit.loading": "Φόρτωση...",
"form.submit.saving": "Αποθήκευση...",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"πριν %d έτος",
"πριν %d έτη"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Ταχύτητα αναπαραγωγής του ήχου/βίντεο",
"error.settings_media_playback_rate_range": "Η ταχύτητα αναπαραγωγής είναι εκτός εύρους",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Are you sure?",
"confirm.question.refresh": "Are you sure you want to force refresh?",
"confirm.yes": "yes",
@ -18,6 +19,8 @@
"action.home_screen": "Add to home screen",
"tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s",
"tooltip.logged_user": "Logged in as %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Unread",
"menu.starred": "Starred",
"menu.history": "History",
@ -32,6 +35,7 @@
"menu.about": "About",
"menu.export": "Export",
"menu.import": "Import",
"menu.search": "Search",
"menu.create_category": "Create a category",
"menu.mark_page_as_read": "Mark this page as read",
"menu.mark_all_as_read": "Mark all as read",
@ -50,6 +54,7 @@
"menu.shared_entries": "Shared entries",
"search.label": "Search",
"search.placeholder": "Search…",
"search.submit": "Search",
"pagination.next": "Next",
"pagination.previous": "Previous",
"entry.status.unread": "Unread",
@ -84,8 +89,24 @@
],
"entry.tags.label": "Tags:",
"page.shared_entries.title": "Shared entries",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Unread",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Starred",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Categories",
"page.categories.no_feed": "No feed.",
"page.categories.entries": "Entries",
@ -94,20 +115,28 @@
"There is %d feed.",
"There are %d feeds."
],
"page.categories.unread_counter": "Number of unread entries",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "New Category",
"page.new_user.title": "New User",
"page.edit_category.title": "Edit Category: %s",
"page.edit_user.title": "Edit User: %s",
"page.feeds.title": "Feeds",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Last check:",
"page.feeds.unread_counter": "Number of unread entries",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Number of read entries",
"page.feeds.error_count": [
"%d error",
"%d errors"
],
"page.history.title": "History",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Import",
"page.search.title": "Search Results",
"page.about.title": "About",
@ -138,7 +167,7 @@
"page.keyboard_shortcuts.subtitle.pages": "Pages Navigation",
"page.keyboard_shortcuts.subtitle.actions": "Actions",
"page.keyboard_shortcuts.go_to_unread": "Go to unread",
"page.keyboard_shortcuts.go_to_starred": "Go to bookmarks",
"page.keyboard_shortcuts.go_to_starred": "Go to starred",
"page.keyboard_shortcuts.go_to_history": "Go to history",
"page.keyboard_shortcuts.go_to_feeds": "Go to feeds",
"page.keyboard_shortcuts.go_to_categories": "Go to categories",
@ -147,6 +176,8 @@
"page.keyboard_shortcuts.go_to_previous_item": "Go to previous item",
"page.keyboard_shortcuts.go_to_next_item": "Go to next item",
"page.keyboard_shortcuts.go_to_feed": "Go to feed",
"page.keyboard_shortcuts.go_to_top_item": "Go to top item",
"page.keyboard_shortcuts.go_to_bottom_item": "Go to bottom item",
"page.keyboard_shortcuts.go_to_previous_page": "Go to previous page",
"page.keyboard_shortcuts.go_to_next_page": "Go to next page",
"page.keyboard_shortcuts.open_item": "Open selected item",
@ -159,7 +190,7 @@
"page.keyboard_shortcuts.refresh_all_feeds": "Refresh all feeds in the background",
"page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read",
"page.keyboard_shortcuts.download_content": "Download original content",
"page.keyboard_shortcuts.toggle_bookmark_status": "Toggle bookmark",
"page.keyboard_shortcuts.toggle_bookmark_status": "Toggle starred",
"page.keyboard_shortcuts.save_article": "Save entry",
"page.keyboard_shortcuts.scroll_item_to_top": "Scroll item to top",
"page.keyboard_shortcuts.remove_feed": "Remove this feed",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Unlink my Google account",
"page.settings.link_oidc_account": "Link my OpenID Connect account",
"page.settings.unlink_oidc_account": "Unlink my OpenID Connect account",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Register passkey",
"page.settings.webauthn.register.error": "Unable to register passkey",
"page.settings.webauthn.delete": [
"Remove %d passkey",
"Remove %d passkeys"
],
"page.login.title": "Sign In",
"page.login.google_signin": "Sign in with Google",
"page.login.oidc_signin": "Sign in with OpenID Connect",
"page.login.webauthn_login": "Login with passkey",
"page.login.webauthn_login.error": "Unable to login with passkey",
"page.integrations.title": "Integrations",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Endpoint",
@ -209,10 +253,12 @@
"page.offline.title": "Offline Mode",
"page.offline.message": "You are offline",
"page.offline.refresh_page": "Try to refresh the page",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "There is no shared entry.",
"alert.no_bookmark": "There is no bookmark at the moment.",
"alert.no_bookmark": "There are no starred entries.",
"alert.no_category": "There is no category.",
"alert.no_category_entry": "There are no entries in this category.",
"alert.no_tag_entry": "There are no entries matching this tag.",
"alert.no_feed_entry": "There are no entries for this feed.",
"alert.no_feed": "You dont have any feeds.",
"alert.no_feed_in_category": "There is no feed for this category.",
@ -271,6 +317,7 @@
"form.feed.label.title": "Title",
"form.feed.label.site_url": "Site URL",
"form.feed.label.feed_url": "Feed URL",
"form.feed.label.description": "Description",
"form.feed.label.category": "Category",
"form.feed.label.crawler": "Fetch original content",
"form.feed.label.feed_username": "Feed Username",
@ -285,6 +332,7 @@
"form.feed.label.urlrewrite_rules": "URL Rewrite Rules",
"form.feed.label.ignore_http_cache": "Ignore HTTP cache",
"form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Fetch via proxy",
"form.feed.label.disabled": "Do not refresh this feed",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API key",
"form.integration.omnivore_activate": "Save entries to Omnivore",
"form.integration.omnivore_api_key": "Omnivore API key",
"form.integration.omnivore_url": "Omnivore API Endpoint",
"form.integration.espial_activate": "Save entries to Espial",
"form.integration.espial_endpoint": "Espial API Endpoint",
"form.integration.espial_api_key": "Espial API key",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Save entries to Linkding",
"form.integration.linkding_endpoint": "Linkding API Endpoint",
"form.integration.linkding_api_key": "Linkding API key",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Mark bookmark as unread",
"form.integration.linkwarden_activate": "Save entries to Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden API key",
"form.integration.matrix_bot_activate": "Push new entries to Matrix",
"form.integration.matrix_bot_user": "Username for Matrix",
"form.integration.matrix_bot_password": "Password for Matrix user",
"form.integration.matrix_bot_url": "Matrix server URL",
"form.integration.matrix_bot_chat_id": "ID of Matrix Room",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Save entries to readeck",
"form.integration.readeck_endpoint": "Readeck API Endpoint",
"form.integration.readeck_api_key": "Readeck API key",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Send only URL (instead of full content)",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Loading…",
"form.submit.saving": "Saving…",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"%d year ago",
"%d years ago"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Playback speed of the audio/video",
"error.settings_media_playback_rate_range": "Playback speed is out of range",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "¿Estás seguro?",
"confirm.question.refresh": "¿Quieres forzar la actualización?",
"confirm.yes": "sí",
@ -18,6 +19,8 @@
"action.home_screen": "Añadir a la pantalla principal",
"tooltip.keyboard_shortcuts": "Atajo de teclado: %s",
"tooltip.logged_user": "Registrado como %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "No leídos",
"menu.starred": "Marcadores",
"menu.history": "Historial",
@ -32,6 +35,7 @@
"menu.about": "Acerca de",
"menu.export": "Exportar",
"menu.import": "Importar",
"menu.search": "Buscar",
"menu.create_category": "Crear una categoría",
"menu.mark_page_as_read": "Marcar esta página como leída",
"menu.mark_all_as_read": "Marcar todos como leídos",
@ -50,6 +54,7 @@
"menu.shared_entries": "Artículos compartidos",
"search.label": "Buscar",
"search.placeholder": "Búsqueda...",
"search.submit": "Search",
"pagination.next": "Siguiente",
"pagination.previous": "Anterior",
"entry.status.unread": "No leído",
@ -81,11 +86,27 @@
"entry.estimated_reading_time": [
"%d minuto de lectura",
"%d minutos de lectura"
],
],
"entry.tags.label": "Etiquetas:",
"page.shared_entries.title": "Artículos compartidos",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "No leídos",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Marcadores",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Categorías",
"page.categories.no_feed": "Sin fuente.",
"page.categories.entries": "Artículos",
@ -94,20 +115,28 @@
"Hay %d fuente.",
"Hay %d fuentes."
],
"page.categories.unread_counter": "Número de artículos no leídos",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "Nueva categoría",
"page.new_user.title": "Nuevo usuario",
"page.edit_category.title": "Editar categoría: %s",
"page.edit_user.title": "Editar usuario: %s",
"page.feeds.title": "Fuentes",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Última verificación:",
"page.feeds.unread_counter": "Número de artículos no leídos",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Número de artículos leídos",
"page.feeds.error_count": [
"%d error",
"%d errores"
],
"page.history.title": "Historial",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Importar",
"page.search.title": "Resultados de la búsqueda",
"page.about.title": "Acerca de",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Ir a la fuente",
"page.keyboard_shortcuts.go_to_previous_page": "Ir al página anterior",
"page.keyboard_shortcuts.go_to_next_page": "Ir al página siguiente",
"page.keyboard_shortcuts.go_to_bottom_item": "Ir al elemento inferior",
"page.keyboard_shortcuts.go_to_top_item": "Ir al elemento superior",
"page.keyboard_shortcuts.open_item": "Abrir el elemento seleccionado",
"page.keyboard_shortcuts.open_original": "Abrir el enlace original",
"page.keyboard_shortcuts.open_original_same_window": "Abrir enlace original en la pestaña actual",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Desvincular mi cuenta de Google",
"page.settings.link_oidc_account": "Vincular mi cuenta de OpenID Connect",
"page.settings.unlink_oidc_account": "Desvincular mi cuenta de OpenID Connect",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Registrar clave de acceso",
"page.settings.webauthn.register.error": "No se puede registrar la clave de paso",
"page.settings.webauthn.delete": [
"Eliminar %d clave de paso",
"Eliminar %d claves de paso"
],
"page.login.title": "Iniciar sesión",
"page.login.google_signin": "Iniciar sesión con tu cuenta de Google",
"page.login.oidc_signin": "Iniciar sesión con tu cuenta de OpenID Connect",
"page.login.webauthn_login": "Iniciar sesión con clave de acceso",
"page.login.webauthn_login.error": "No se puede iniciar sesión con la clave de paso",
"page.integrations.title": "Integraciones",
"page.integration.miniflux_api": "API de Miniflux",
"page.integration.miniflux_api_endpoint": "Extremo de API",
@ -209,10 +253,12 @@
"page.offline.title": "Modo offline",
"page.offline.message": "Estas desconectado",
"page.offline.refresh_page": "Intenta actualizar la página",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "No hay artículos compartidos.",
"alert.no_bookmark": "No hay marcador en este momento.",
"alert.no_category": "No hay categoría.",
"alert.no_category_entry": "No hay artículos en esta categoría.",
"alert.no_tag_entry": "No hay artículos con esta etiqueta.",
"alert.no_feed_entry": "No hay artículos para esta fuente.",
"alert.no_feed": "No tienes fuentes.",
"alert.no_feed_in_category": "No hay fuentes para esta categoría.",
@ -271,6 +317,7 @@
"form.feed.label.title": "Título",
"form.feed.label.site_url": "URL del sitio",
"form.feed.label.feed_url": "URL de la fuente",
"form.feed.label.description": "Descripción",
"form.feed.label.category": "Categoría",
"form.feed.label.crawler": "Obtener rastreador original",
"form.feed.label.feed_username": "Nombre de usuario de la fuente",
@ -285,6 +332,7 @@
"form.feed.label.urlrewrite_rules": "Reglas de Filtrado (Reescritura)",
"form.feed.label.ignore_http_cache": "Ignorar caché HTTP",
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Buscar a través de proxy",
"form.feed.label.disabled": "No actualice este feed",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Enviar artículos a Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Acceso API de Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Clave de API de Nunux Keeper",
"form.integration.omnivore_activate": "Enviar artículos a Omnivore",
"form.integration.omnivore_url": "Acceso API de Omnivore",
"form.integration.omnivore_api_key": "Clave de API de Omnivore",
"form.integration.espial_activate": "Enviar artículos a Espial",
"form.integration.espial_endpoint": "Acceso API de Espial",
"form.integration.espial_api_key": "Clave de API de Espial",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Enviar artículos a Linkding",
"form.integration.linkding_endpoint": "Acceso API de Linkding",
"form.integration.linkding_api_key": "Clave de API de Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Marcar marcador como no leído",
"form.integration.linkwarden_activate": "Enviar artículos a Linkwarden",
"form.integration.linkwarden_endpoint": "Acceso API de Linkwarden",
"form.integration.linkwarden_api_key": "Clave de API de Linkwarden",
"form.integration.matrix_bot_activate": "Transferir nuevos artículos a Matrix",
"form.integration.matrix_bot_user": "Nombre de usuario para Matrix",
"form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix",
"form.integration.matrix_bot_url": "URL del servidor de Matrix",
"form.integration.matrix_bot_chat_id": "ID de la sala de Matrix",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Enviar artículos a Readeck",
"form.integration.readeck_endpoint": "Acceso API de Readeck",
"form.integration.readeck_api_key": "Clave de API de Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Enviar solo URL (en lugar de contenido completo)",
"form.integration.shiori_activate": "Guardar artículos a Shiori",
"form.integration.shiori_endpoint": "Extremo de API de Shiori",
"form.integration.shiori_username": "Nombre de usuario de Shiori",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "Etiqueta de clave API",
"form.submit.loading": "Cargando...",
"form.submit.saving": "Guardando...",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"hace %d año",
"hace %d años"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Velocidad de reproducción del audio/vídeo",
"error.settings_media_playback_rate_range": "La velocidad de reproducción está fuera de rango",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Oletko varma?",
"confirm.question.refresh": "Haluatko pakottaa päivityksen?",
"confirm.yes": "kyllä",
@ -18,6 +19,8 @@
"action.home_screen": "Lisää aloitusnäytölle",
"tooltip.keyboard_shortcuts": "Pikanäppäin: %s",
"tooltip.logged_user": "Kirjautunut %s-käyttäjänä",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Lukemattomat",
"menu.starred": "Suosikit",
"menu.history": "Historia",
@ -32,6 +35,7 @@
"menu.about": "Tietoja",
"menu.export": "Vie",
"menu.import": "Tuo",
"menu.search": "Haku",
"menu.create_category": "Luo kategoria",
"menu.mark_page_as_read": "Merkitse tämä sivu luetuksi",
"menu.mark_all_as_read": "Merkitse kaikki luetuksi",
@ -50,6 +54,7 @@
"menu.shared_entries": "Jaetut artikkelit",
"search.label": "Haku",
"search.placeholder": "Hae...",
"search.submit": "Search",
"pagination.next": "Seuraava",
"pagination.previous": "Edellinen",
"entry.status.unread": "Lukematon",
@ -84,8 +89,24 @@
],
"entry.tags.label": "Tags:",
"page.shared_entries.title": "Jaetut artikkelit",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Lukemattomat",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Suosikit",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Kategoriat",
"page.categories.no_feed": "Ei syötettä.",
"page.categories.entries": "Artikkelit",
@ -94,20 +115,28 @@
"On %d syöte.",
"On %d syötettä."
],
"page.categories.unread_counter": "Lukemattomien artikkeleiden määrä",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "Uusi kategoria",
"page.new_user.title": "Uusi käyttäjä",
"page.edit_category.title": "Muokkaa kategoria: %s",
"page.edit_user.title": "Muokkaa käyttäjä: %s",
"page.feeds.title": "Syötteet",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Viimeisin tarkistus:",
"page.feeds.unread_counter": "Lukemattomien artikkeleiden määrä",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Luettujen artikkeleiden määrä",
"page.feeds.error_count": [
"%d virhe",
"%d virhettä"
],
"page.history.title": "Historia",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Tuo",
"page.search.title": "Hakutulokset",
"page.about.title": "Tietoja",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Siirry syötteeseen",
"page.keyboard_shortcuts.go_to_previous_page": "Siirry edelliselle sivulle",
"page.keyboard_shortcuts.go_to_next_page": "Siirry seuraavalle sivulle",
"page.keyboard_shortcuts.go_to_bottom_item": "Siirry alimpaan kohtaan",
"page.keyboard_shortcuts.go_to_top_item": "Siirry alkuun",
"page.keyboard_shortcuts.open_item": "Avaa valittu kohde",
"page.keyboard_shortcuts.open_original": "Avaa alkuperäinen linkki",
"page.keyboard_shortcuts.open_original_same_window": "Avaa alkuperäinen linkki nykyisessä välilehdessä",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Poista Google-tilini linkitys",
"page.settings.link_oidc_account": "Linkitä OpenID Connect -tilini",
"page.settings.unlink_oidc_account": "Poista OpenID Connect -tilini linkitys",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Rekisteröi salasana",
"page.settings.webauthn.register.error": "Salasanaa ei voi rekisteröidä",
"page.settings.webauthn.delete": [
"Poista %d salasana",
"Poista %d salasanaa"
],
"page.login.title": "Kirjaudu sisään",
"page.login.google_signin": "Kirjaudu sisään Googlella",
"page.login.oidc_signin": "Kirjaudu sisään OpenID Connectilla",
"page.login.webauthn_login": "Kirjaudu sisään salasanalla",
"page.login.webauthn_login.error": "Ei voida kirjautua sisään salasanalla",
"page.integrations.title": "Integraatiot",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API-päätepiste",
@ -209,10 +253,12 @@
"page.offline.title": "Offline-tila",
"page.offline.message": "Olet offline-tilassa",
"page.offline.refresh_page": "Yritä päivittää sivu",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Jaettua artikkelia ei ole.",
"alert.no_bookmark": "Tällä hetkellä ei ole kirjanmerkkiä.",
"alert.no_category": "Ei ole kategoriaa.",
"alert.no_category_entry": "Tässä kategoriassa ei ole artikkeleita.",
"alert.no_tag_entry": "Tätä tunnistetta vastaavia merkintöjä ei ole.",
"alert.no_feed_entry": "Tässä syötteessä ei ole artikkeleita.",
"alert.no_feed": "Sinulla ei ole tilauksia.",
"alert.no_feed_in_category": "Tälle kategorialle ei ole tilausta.",
@ -273,6 +319,7 @@
"form.feed.label.title": "Otsikko",
"form.feed.label.site_url": "Sivuston URL-osoite",
"form.feed.label.feed_url": "Syötteen URL-osoite",
"form.feed.label.description": "Kuvaus",
"form.feed.label.category": "Kategoria",
"form.feed.label.crawler": "Nouda alkuperäinen sisältö",
"form.feed.label.feed_username": "Syötteen käyttäjätunnus",
@ -285,6 +332,7 @@
"form.feed.label.keeplist_rules": "Keep-säännöt",
"form.feed.label.ignore_http_cache": "Ohita HTTP-välimuisti",
"form.feed.label.allow_self_signed_certificates": "Salli itseallekirjoitetut tai virheelliset varmenteet",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Nouda välityspalvelimen kautta",
"form.feed.label.disabled": "Älä päivitä tätä syötettä",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Tallenna artikkelit Nunux Keeperiin",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API-päätepiste",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-avain",
"form.integration.omnivore_activate": "Tallenna artikkelit Omnivoreiin",
"form.integration.omnivore_url": "Omnivore API-päätepiste",
"form.integration.omnivore_api_key": "Omnivore API-avain",
"form.integration.espial_activate": "Tallenna artikkelit Espialiin",
"form.integration.espial_endpoint": "Espial API-päätepiste",
"form.integration.espial_api_key": "Espial API-avain",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Tallenna artikkelit Linkkiin",
"form.integration.linkding_endpoint": "Linkding API-päätepiste",
"form.integration.linkding_api_key": "Linkding API-avain",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Merkitse kirjanmerkki lukemattomaksi",
"form.integration.linkwarden_activate": "Tallenna artikkelit Linkkiin",
"form.integration.linkwarden_endpoint": "Linkwarden API-päätepiste",
"form.integration.linkwarden_api_key": "Linkwarden API-avain",
"form.integration.matrix_bot_activate": "Siirrä uudet artikkelit Matrixiin",
"form.integration.matrix_bot_user": "Matrixin käyttäjätunnus",
"form.integration.matrix_bot_password": "Matrix-käyttäjän salasana",
"form.integration.matrix_bot_url": "Matrix-palvelimen URL-osoite",
"form.integration.matrix_bot_chat_id": "Matrix-huoneen tunnus",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Tallenna artikkelit Readeckiin",
"form.integration.readeck_endpoint": "Readeck API-päätepiste",
"form.integration.readeck_api_key": "Readeck API-avain",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Lähetä vain URL-osoite (koko sisällön sijaan)",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Ladataan...",
"form.submit.saving": "Tallennetaan...",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"%d vuosi sitten",
"%d vuotta sitten"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Äänen/videon toistonopeus",
"error.settings_media_playback_rate_range": "Toistonopeus on alueen ulkopuolella",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Aller au contenu",
"confirm.question": "Êtes-vous sûr ?",
"confirm.question.refresh": "Voulez-vous forcer le rafraîchissement ?",
"confirm.yes": "oui",
@ -18,6 +19,8 @@
"action.home_screen": "Ajouter à l'écran d'accueil",
"tooltip.keyboard_shortcuts": "Raccourci clavier : %s",
"tooltip.logged_user": "Connecté en tant que %s",
"menu.title": "Menu",
"menu.home_page": "Page d'accueil",
"menu.unread": "Non lus",
"menu.starred": "Favoris",
"menu.history": "Historique",
@ -32,6 +35,7 @@
"menu.about": "À propos",
"menu.export": "Export",
"menu.import": "Import",
"menu.search": "Recherche",
"menu.create_category": "Créer une catégorie",
"menu.mark_page_as_read": "Marquer cette page comme lu",
"menu.mark_all_as_read": "Tout marquer comme lu",
@ -50,6 +54,7 @@
"menu.shared_entries": "Articles partagés",
"search.label": "Recherche",
"search.placeholder": "Recherche...",
"search.submit": "Rechercher",
"pagination.next": "Suivant",
"pagination.previous": "Précédent",
"entry.status.unread": "Non lu",
@ -81,11 +86,27 @@
"entry.estimated_reading_time": [
"%d minute de lecture",
"%d minutes de lecture"
],
],
"entry.tags.label": "Libellés :",
"page.shared_entries.title": "Articles partagés",
"page.shared_entries_count": [
"%d article partagé",
"%d articles partagés"
],
"page.unread.title": "Non lus",
"page.unread_entry_count": [
"%d article non lu",
"%d articles non lus"
],
"page.total_entry_count": [
"%d article au total",
"%d articles au total"
],
"page.starred.title": "Favoris",
"page.starred_entry_count": [
"%d favori",
"%d favoris"
],
"page.categories.title": "Catégories",
"page.categories.no_feed": "Aucun abonnement.",
"page.categories.entries": "Articles",
@ -94,20 +115,28 @@
"Il y a %d abonnement.",
"Il y a %d abonnements."
],
"page.categories.unread_counter": "Nombre d'entrées non lues",
"page.categories_count": [
"%d catégorie",
"%d catégories"
],
"page.new_category.title": "Nouvelle catégorie",
"page.new_user.title": "Nouvel Utilisateur",
"page.edit_category.title": "Modification de la catégorie : %s",
"page.edit_user.title": "Modification de l'utilisateur : %s",
"page.feeds.title": "Abonnements",
"page.category_label": "Catégorie : %s",
"page.feeds.last_check": "Dernière vérification :",
"page.feeds.unread_counter": "Nombre d'entrées non lues",
"page.feeds.next_check": "Prochaine vérification :",
"page.feeds.read_counter": "Nombre d'entrées lues",
"page.feeds.error_count": [
"%d erreur",
"%d erreurs"
],
"page.history.title": "Historique",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Importation",
"page.search.title": "Résultats de la recherche",
"page.about.title": "À propos",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Voir abonnement",
"page.keyboard_shortcuts.go_to_previous_page": "Page précédente",
"page.keyboard_shortcuts.go_to_next_page": "Page suivante",
"page.keyboard_shortcuts.go_to_bottom_item": "Aller à l'élément du bas",
"page.keyboard_shortcuts.go_to_top_item": "Aller à l'élément supérieur",
"page.keyboard_shortcuts.open_item": "Ouvrir élément sélectionné",
"page.keyboard_shortcuts.open_original": "Ouvrir le lien original",
"page.keyboard_shortcuts.open_original_same_window": "Ouvrir le lien original dans l'onglet en cours",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Dissocier mon compte Google",
"page.settings.link_oidc_account": "Associer mon compte OpenID Connect",
"page.settings.unlink_oidc_account": "Dissocier mon compte OpenID Connect",
"page.settings.webauthn.passkeys": "Clés daccès",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Nom de la clé daccès",
"page.settings.webauthn.added_on": "Date de création",
"page.settings.webauthn.last_seen_on": "Dernière utilisation",
"page.settings.webauthn.register": "Enregister une nouvelle clé daccès",
"page.settings.webauthn.register.error": "Impossible d'enregistrer la clé daccès",
"page.settings.webauthn.delete": [
"Supprimer %d clé daccès",
"Supprimer %d clés daccès"
],
"page.login.title": "Connexion",
"page.login.google_signin": "Se connecter avec Google",
"page.login.oidc_signin": "Se connecter avec OpenID Connect",
"page.login.webauthn_login": "Se connecter avec une clé daccès",
"page.login.webauthn_login.error": "Impossible de se connecter avec la clé daccès",
"page.integrations.title": "Intégrations",
"page.integration.miniflux_api": "API de Miniflux",
"page.integration.miniflux_api_endpoint": "Point de terminaison de l'API",
@ -209,10 +253,12 @@
"page.offline.title": "Mode Hors-Ligne",
"page.offline.message": "Vous n'êtes pas connecté",
"page.offline.refresh_page": "Essayez de rafraîchir la page",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Il n'y a pas d'article partagé.",
"alert.no_bookmark": "Il n'y a aucun favoris pour le moment.",
"alert.no_category": "Il n'y a aucune catégorie.",
"alert.no_category_entry": "Il n'y a aucun article dans cette catégorie.",
"alert.no_tag_entry": "Il n'y a aucun article correspondant à ce tag.",
"alert.no_feed_entry": "Il n'y a aucun article pour cet abonnement.",
"alert.no_feed": "Vous n'avez aucun abonnement.",
"alert.no_feed_in_category": "Il n'y a pas d'abonnement pour cette catégorie.",
@ -271,6 +317,7 @@
"form.feed.label.title": "Titre",
"form.feed.label.site_url": "URL du site web",
"form.feed.label.feed_url": "URL du flux",
"form.feed.label.description": "Description",
"form.feed.label.category": "Catégorie",
"form.feed.label.crawler": "Récupérer le contenu original",
"form.feed.label.feed_username": "Nom d'utilisateur du flux",
@ -285,6 +332,7 @@
"form.feed.label.urlrewrite_rules": "Règles de réécriture d'URL",
"form.feed.label.ignore_http_cache": "Ignorer le cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Autoriser les certificats auto-signés ou non valides",
"form.feed.label.disable_http2": "Désactiver HTTP/2",
"form.feed.label.fetch_via_proxy": "Récupérer via proxy",
"form.feed.label.disabled": "Ne pas actualiser ce flux",
"form.feed.label.no_media_player": "Pas de lecteur multimedia (audio/vidéo)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Sauvegarder les articles vers Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "URL de l'API de Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Clé d'API de Nunux Keeper",
"form.integration.omnivore_activate": "Sauvegarder les articles vers Omnivore",
"form.integration.omnivore_url": "URL de l'API de Omnivore",
"form.integration.omnivore_api_key": "Clé d'API de Omnivore",
"form.integration.espial_activate": "Sauvegarder les articles vers Espial",
"form.integration.espial_endpoint": "URL de l'API de Espial",
"form.integration.espial_api_key": "Clé d'API de Espial",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Désactiver l'aperçu de la page Web",
"form.integration.telegram_bot_disable_notification": "Désactiver les notifications",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Sauvegarder les articles vers Linkding",
"form.integration.linkding_endpoint": "URL de l'API de Linkding",
"form.integration.linkding_api_key": "Clé d'API de Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Marquer le lien comme non lu",
"form.integration.linkwarden_activate": "Sauvegarder les articles vers Linkwarden",
"form.integration.linkwarden_endpoint": "URL de l'API de Linkwarden",
"form.integration.linkwarden_api_key": "Clé d'API de Linkwarden",
"form.integration.matrix_bot_activate": "Envoyer les nouveaux articles vers Matrix",
"form.integration.matrix_bot_user": "Nom de l'utilisateur Matrix",
"form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix",
"form.integration.matrix_bot_url": "URL du serveur Matrix",
"form.integration.matrix_bot_chat_id": "Identifiant de la salle Matrix",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Sauvegarder les articles vers Readeck",
"form.integration.readeck_endpoint": "URL de l'API de Readeck",
"form.integration.readeck_api_key": "Clé d'API de Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Envoyer uniquement l'URL (au lieu du contenu complet)",
"form.integration.shiori_activate": "Sauvegarder les articles vers Shiori",
"form.integration.shiori_endpoint": "URL de l'API de Shiori",
"form.integration.shiori_username": "Nom d'utilisateur de Shiori",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Activer le webhook",
"form.integration.webhook_url": "URL du webhook",
"form.integration.webhook_secret": "Secret du webhook",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "Libellé de la clé d'API",
"form.submit.loading": "Chargement...",
"form.submit.saving": "Sauvegarde en cours...",
@ -433,23 +504,44 @@
"il y a %d an",
"il y a %d ans"
],
"This feed already exists (%s)": "Cet abonnement existe déjà (%s)",
"Unable to fetch feed (Status Code = %d)": "Impossible de récupérer cet abonnement (code=%d)",
"Unable to open this link: %v": "Impossible d'ouvrir ce lien : %v",
"Unable to analyze this page: %v": "Impossible d'analyzer cette page : %v",
"Unable to execute request: %v": "Impossible d'exécuter cette requête: %v",
"Unable to parse OPML file: %q": "Impossible de lire ce fichier OPML : %q",
"Unable to parse RSS feed: %q": "Impossible de lire ce flux RSS : %q",
"Unable to parse Atom feed: %q": "Impossible de lire ce flux Atom : %q",
"Unable to parse JSON feed: %q": "Impossible de lire ce flux JSON : %q",
"Unable to parse RDF feed: %q": "Impossible de lire ce flux RDF : %q",
"Unable to normalize encoding: %q": "Impossible de normaliser l'encodage : %q",
"This feed is empty": "Cet abonnement est vide",
"This web page is empty": "Cette page web est vide",
"Invalid SSL certificate (original error: %q)": "Certificat SSL invalide (erreur originale : %q)",
"This website is unreachable (original error: %q)": "Ce site web n'est pas joignable (erreur originale : %q)",
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes",
"You are not authorized to access this resource (invalid username/password)": "Vous n'êtes pas autorisé à accéder à cette ressource (nom d'utilisateur / mot de passe incorrect)",
"Unable to fetch this resource (Status Code = %d)": "Impossible de récupérer cette ressource (code=%d)",
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux"
"alert.too_many_feeds_refresh": [
"Vous avez déclenché trop d'actualisations de flux. Veuillez attendre %d minute avant de réessayer.",
"Vous avez déclenché trop d'actualisations de flux. Veuillez attendre %d minutes avant de réessayer."
],
"alert.background_feed_refresh": "Les abonnements sont en cours d'actualisation en arrière-plan. Vous pouvez continuer à naviguer dans l'application.",
"error.http_response_too_large": "La réponse HTTP est trop volumineuse. Vous pouvez augmenter la limite de taille de réponse HTTP dans les paramètres de l'application (redémarrage de l'application nécessaire).",
"error.http_body_read": "Impossible de lire le corps de la réponse HTTP : %v.",
"error.http_empty_response_body": "Le corps de la réponse HTTP est vide.",
"error.http_empty_response": "La réponse HTTP est vide. Peut-être que ce site web bloque Miniflux avec une protection anti-bot ?",
"error.tls_error": "Erreur TLS : %q. Vous pouvez désactiver la vérification TLS dans les paramètres de l'abonnement.",
"error.network_operation": "Miniflux n'est pas en mesure de se connecter à ce site web à cause d'un problème réseau : %v.",
"error.network_timeout": "Ce site web est trop lent à répondre : %v.",
"error.http_client_error": "Erreur du client HTTP : %v.",
"error.http_not_authorized": "Accès non autorisé à ce site web. Veuillez vérifier les identifiants de cet abonnement.",
"error.http_too_many_requests": "Miniflux a généré trop de requêtes vers ce site web. Veuillez réessayer plus tard ou changez la configuration de l'application.",
"error.http_forbidden": "Accès interdit à ce site web. Il se peut que ce site web bloque Miniflux avec une protection anti-bot.",
"error.http_resource_not_found": "La resource demandée n'existe pas sur ce site web. Veuillez vérifier l'URL.",
"error.http_internal_server_error": "Le site web n'est pas disponible pour le moment à cause d'une erreur interne au serveur. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
"error.http_bad_gateway": "Le site web n'est pas disponible pour le moment à cause d'une erreur de passerelle réseau. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
"error.http_service_unavailable": "Le site web n'est pas disponible pour le moment. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
"error.http_gateway_timeout": "Le site web n'est pas disponible pour le moment à cause d'un délai d'attente dépassé. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
"error.http_unexpected_status_code": "Le site web a répondu avec un code HTTP inattendu : %d. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
"error.database_error": "Erreur de la base de données : %v.",
"error.category_not_found": "Cette catégorie n'existe pas ou n'appartient pas à cet utilisateur.",
"error.duplicated_feed": "Ce flux existe déjà.",
"error.unable_to_parse_feed": "Impossible d'analyser ce flux : %v.",
"error.feed_not_found": "Impossible de trouver ce flux.",
"error.unable_to_detect_rssbridge": "Impossible de détecter un flux RSS en utilisant RSS-Bridge: %v.",
"error.feed_format_not_detected": "Impossible de détecter le format du flux : %v.",
"form.prefs.label.media_playback_rate": "Vitesse de lecture de l'audio/vidéo",
"error.settings_media_playback_rate_range": "La vitesse de lecture est hors limites",
"enclosure_media_controls.seek" : "Avancer/Reculer :",
"enclosure_media_controls.seek.title" : "Avancer/Reculer de %s seconds",
"enclosure_media_controls.speed" : "Vitesse :",
"enclosure_media_controls.speed.faster" : "Accélérer",
"enclosure_media_controls.speed.faster.title" : "Accélérer de %sx",
"enclosure_media_controls.speed.slower" : "Ralentir",
"enclosure_media_controls.speed.slower.title" : "Ralentir de %sx",
"enclosure_media_controls.speed.reset" : "Réinitialiser",
"enclosure_media_controls.speed.reset.title" : "Réinitialiser la vitesse de lecture à 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "मंजूर है?",
"confirm.question.refresh": "क्या आप बल द्वारा ताज़ा करना चाहते हैं?",
"confirm.yes": "हाँ",
@ -18,6 +19,8 @@
"action.home_screen": "होम स्क्रीन में शामिल करें",
"tooltip.keyboard_shortcuts": "कुंजीपटल संक्षिप्त रीति: %s",
"tooltip.logged_user": "%s के रूप में लॉग इन किया",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "अपठित",
"menu.starred": "तारांकित",
"menu.history": "इतिहास",
@ -32,6 +35,7 @@
"menu.about": "के बारे में",
"menu.export": "निर्यात करे",
"menu.import": "आयात करे",
"menu.search": "खोज",
"menu.create_category": "श्रेणी बनाए",
"menu.mark_page_as_read": "इस पृष्ठ को पढ़ा हुआ चिह्नित करें",
"menu.mark_all_as_read": "सभी को पढ़ा हुआ मार्क करें",
@ -50,6 +54,7 @@
"menu.shared_entries": "साझा प्रविष्टियां",
"search.label": "खोजे",
"search.placeholder": "खोजे...",
"search.submit": "Search",
"pagination.next": "अगला",
"pagination.previous": "पिछला",
"entry.status.unread": "अपठित",
@ -81,11 +86,27 @@
"entry.estimated_reading_time": [
"पढ़ने मे %d मिनट मागेगा",
"पढ़ने मे %d मिनट मागेगा"
],
],
"entry.tags.label": "टैग:",
"page.shared_entries.title": "साझा किया हुआ प्रविष्टि",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "अपठित",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "तारांकित",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "श्रेणियाँ",
"page.categories.no_feed": "कोई फ़ीड नहीं है।",
"page.categories.entries": "विषयवस्तुया",
@ -94,20 +115,28 @@
"%d फ़ीड बाकी है।",
"%d फ़ीड बाकी है।"
],
"page.categories.unread_counter": "अपठित प्रविष्टिया",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "नया श्रेणी",
"page.new_user.title": "नया उपभोक्ता",
"page.edit_category.title": "%s श्रेणी संपाद करे",
"page.edit_user.title": "%s उपभोक्ता संपाद करे",
"page.feeds.title": "फ़ीड",
"page.category_label": "Category: %s",
"page.feeds.last_check": "आखरी जाँच",
"page.feeds.unread_counter": "अपठित विषयवस्तुया",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "पड़े हुए विषयवस्तुया",
"page.feeds.error_count": [
"%d समस्या",
"%d समस्याए"
],
"page.history.title": "इतिहास",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "आयात",
"page.search.title": "खोज का परिणाम",
"page.about.title": "पृष्ठ के बारे में",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "फ़ीड पर जाएं",
"page.keyboard_shortcuts.go_to_previous_page": "पिछले पृष्ठ पर जाएं",
"page.keyboard_shortcuts.go_to_next_page": "अगले पेज पर जाएं",
"page.keyboard_shortcuts.go_to_bottom_item": "निचले आइटम पर जाएँ",
"page.keyboard_shortcuts.go_to_top_item": "शीर्ष आइटम पर जाएँ",
"page.keyboard_shortcuts.open_item": "चयनित आइटम खोलें",
"page.keyboard_shortcuts.open_original": "मूल लिंक खोलें",
"page.keyboard_shortcuts.open_original_same_window": "वर्तमान टैब में मूल लिंक खोलें",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "मेरा गूगल खाता हटाय",
"page.settings.link_oidc_account": "मेरा ओपन-ईद खाता जोरीय",
"page.settings.unlink_oidc_account": "मेरा ओपन-ईद खाता हटाय",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "रजिस्टर पासकी",
"page.settings.webauthn.register.error": "पासकी पंजीकृत करने में असमर्थ",
"page.settings.webauthn.delete": [
"%d पासकुंजी निकालें",
"%d पासकी हटाएं"
],
"page.login.title": "साइन इन करें",
"page.login.google_signin": "गूगल के साथ साइन इन करें",
"page.login.oidc_signin": "ओपन-ईद के साथ साइन इन करें",
"page.login.webauthn_login": "पासकी से लॉगिन करें",
"page.login.webauthn_login.error": "पासकी से लॉगिन करने में असमर्थ",
"page.integrations.title": "एकीकरण",
"page.integration.miniflux_api": "मिनिफलक्ष एपीआई",
"page.integration.miniflux_api_endpoint": "एपीआई समापन बिंदु",
@ -209,10 +253,12 @@
"page.offline.title": "ऑफ़लाइन मोड",
"page.offline.message": "आप संपर्क में नहीं हैं",
"page.offline.refresh_page": "पृष्ठ को ताज़ा करने का प्रयास करें",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "कोई साझा प्रविष्टि नहीं है",
"alert.no_bookmark": "इस समय कोई बुकमार्क नहीं है",
"alert.no_category": "कोई श्रेणी नहीं है।",
"alert.no_category_entry": "इस श्रेणी में कोई विषय-वस्तु नहीं है।",
"alert.no_tag_entry": "इस टैग से मेल खाती कोई प्रविष्टियाँ नहीं हैं।",
"alert.no_feed_entry": "इस फ़ीड के लिए कोई विषय-वस्तु नहीं है।",
"alert.no_feed": "आपके पास कोई सदस्यता नहीं है।",
"alert.no_feed_in_category": "इस श्रेणी के लिए कोई सदस्यता नहीं है।",
@ -271,6 +317,7 @@
"form.feed.label.title": "शीर्षक",
"form.feed.label.site_url": "साइट यूआरएल",
"form.feed.label.feed_url": "फ़ीड यूआरएल",
"form.feed.label.description": "विवरण",
"form.feed.label.category": "श्रेणी",
"form.feed.label.crawler": "मूल सामग्री प्राप्त करें",
"form.feed.label.feed_username": "फ़ीड उपयोगकर्ता नाम",
@ -285,6 +332,7 @@
"form.feed.label.urlrewrite_rules": " यूआरएल पुनर्लेखन नियम",
"form.feed.label.ignore_http_cache": "एचटीटीपी कैश पर ध्यान न दें",
"form.feed.label.allow_self_signed_certificates": "स्व-हस्ताक्षरित या अमान्य प्रमाणपत्रों की अनुमति दें",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "प्रॉक्सी के माध्यम से प्राप्त करें",
"form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -348,7 +396,7 @@
"form.integration.pinboard_bookmark": "बुकमार्क को अपठित के रूप में चिह्नित करें",
"form.integration.instapaper_activate": "विषय-वस्तु को इंस्टापेपर में सहेजें",
"form.integration.instapaper_username": "इंस्टापेपर यूजरनेम",
"form.integration.instapaper_password": "इंस्टापेपर पासवर्ड",
"form.integration.instapaper_password": "इंस्टापेपर पासवर्ड",
"form.integration.pocket_activate": "विषय-कविता को पॉकेट में सहेजें",
"form.integration.pocket_consumer_key": "पॉकेट उपभोक्ता कुंजी",
"form.integration.pocket_access_token": "पॉकेट एक्सेस टोकन",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "विषय-वस्तु को ननक्स कीपर में सहेजें",
"form.integration.nunux_keeper_endpoint": "ननक्स कीपर एपीआई समापन बिंदु",
"form.integration.nunux_keeper_api_key": "ननक्स कीपर एपीआई कुंजी",
"form.integration.omnivore_activate": "Save entries to Omnivore",
"form.integration.omnivore_api_key": "Omnivore API key",
"form.integration.omnivore_url": "Omnivore API Endpoint",
"form.integration.espial_activate": "विषय-वस्तु को जासूसी में सहेजें",
"form.integration.espial_endpoint": "जासूसी एपीआई समापन बिंदु",
"form.integration.espial_api_key": "जासूसी एपीआई कुंजी",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "लिंक्डिन में विषयवस्तु सहेजें",
"form.integration.linkding_endpoint": "लिंकिंग एपीआई समापन बिंदु",
"form.integration.linkding_api_key": "लिंकिंग एपीआई कुंजी",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "बुकमार्क को अपठित के रूप में चिह्नित करें",
"form.integration.linkwarden_activate": "Save entries to Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden API key",
"form.integration.matrix_bot_activate": "नए लेखों को मैट्रिक्स में स्थानांतरित करें",
"form.integration.matrix_bot_user": "मैट्रिक्स के लिए उपयोगकर्ता नाम",
"form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड",
"form.integration.matrix_bot_url": "मैट्रिक्स सर्वर URL",
"form.integration.matrix_bot_chat_id": "मैट्रिक्स रूम की आईडी",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Readeck में विषयवस्तु सहेजें",
"form.integration.readeck_endpoint": "Readeck·एपीआई·समापन·बिंदु",
"form.integration.readeck_api_key": "Readeck एपीआई कुंजी",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "केवल URL भेजें (पूर्ण सामग्री के बजाय)",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "एपीआई कुंजी लेबल",
"form.submit.loading": "लोड हो रहा है...",
"form.submit.saving": "सहेजा जा रहा है...",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"%d साल पहले",
"%d वर्षों पहले"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "ऑडियो/वीडियो की प्लेबैक गति",
"error.settings_media_playback_rate_range": "प्लेबैक गति सीमा से बाहर है",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Apakah Anda yakin?",
"confirm.question.refresh": "Apakah Anda ingin memaksa penyegaran?",
"confirm.yes": "ya",
@ -18,6 +19,8 @@
"action.home_screen": "Tambahkan ke beranda",
"tooltip.keyboard_shortcuts": "Pintasan Papan Tik: %s",
"tooltip.logged_user": "Masuk sebagai %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Belum Dibaca",
"menu.starred": "Markah",
"menu.history": "Riwayat",
@ -32,6 +35,7 @@
"menu.about": "Tentang",
"menu.export": "Ekspor",
"menu.import": "Impor",
"menu.search": "Cari",
"menu.create_category": "Buat kategori",
"menu.mark_page_as_read": "Tandai halaman ini sebagai telah dibaca",
"menu.mark_all_as_read": "Tandai semua sebagai telah dibaca",
@ -50,6 +54,7 @@
"menu.shared_entries": "Entri yang Dibagikan",
"search.label": "Cari",
"search.placeholder": "Cari...",
"search.submit": "Search",
"pagination.next": "Berikutnya",
"pagination.previous": "Sebelumnya",
"entry.status.unread": "Belum dibaca",
@ -79,32 +84,50 @@
"entry.shared_entry.title": "Buka tautan publik",
"entry.shared_entry.label": "Bagikan",
"entry.estimated_reading_time": [
"%d menit untuk dibaca"
],
"%d menit untuk dibaca"
],
"entry.tags.label": "Tanda:",
"page.shared_entries.title": "Entri yang Dibagikan",
"page.shared_entries_count": [
"%d shared entry"
],
"page.unread.title": "Belum Dibaca",
"page.unread_entry_count": [
"%d unread entry"
],
"page.total_entry_count": [
"%d entry in total"
],
"page.starred.title": "Markah",
"page.starred_entry_count": [
"%d starred entry"
],
"page.categories.title": "Kategori",
"page.categories.no_feed": "Tidak ada umpan.",
"page.categories.entries": "Artikel",
"page.categories.feeds": "Langganan",
"page.categories.feed_count": [
"Ada %d umpan."
"Ada %d umpan."
],
"page.categories_count": [
"%d category"
],
"page.categories.unread_counter": "Jumlah entri yang belum dibaca",
"page.new_category.title": "Kategori Baru",
"page.new_user.title": "Pengguna Baru",
"page.edit_category.title": "Sunting Kategori: %s",
"page.edit_user.title": "Sunting Pengguna: %s",
"page.feeds.title": "Umpan",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Terakhir diperiksa:",
"page.feeds.unread_counter": "Jumlah entri yang belum dibaca",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Jumlah entri yang telah dibaca",
"page.feeds.error_count": [
"%d galat"
"%d galat"
],
"page.history.title": "Riwayat",
"page.read_entry_count": [
"%d read entry"
],
"page.import.title": "Impor",
"page.search.title": "Hasil Pencarian",
"page.about.title": "Tentang",
@ -146,6 +169,8 @@
"page.keyboard_shortcuts.go_to_feed": "Ke umpan",
"page.keyboard_shortcuts.go_to_previous_page": "Ke halaman sebelumnya",
"page.keyboard_shortcuts.go_to_next_page": "Ke halaman berikutnya",
"page.keyboard_shortcuts.go_to_bottom_item": "Pergi ke item paling bawah",
"page.keyboard_shortcuts.go_to_top_item": "Pergi ke item teratas",
"page.keyboard_shortcuts.open_item": "Buka entri yang dipilih",
"page.keyboard_shortcuts.open_original": "Buka tautan asli",
"page.keyboard_shortcuts.open_original_same_window": "Buka tautan asli di bilah saat ini",
@ -176,9 +201,21 @@
"page.settings.unlink_google_account": "Putuskan akun Google saya",
"page.settings.link_oidc_account": "Tautkan akun OpenID Connect saya",
"page.settings.unlink_oidc_account": "Putuskan akun OpenID Connect saya",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Register passkey",
"page.settings.webauthn.register.error": "Unable to register passkey",
"page.settings.webauthn.delete": [
"Remove %d passkey"
],
"page.login.title": "Masuk",
"page.login.google_signin": "Masuk dengan Google",
"page.login.oidc_signin": "Masuk dengan OpenID Connect",
"page.login.webauthn_login": "Login with passkey",
"page.login.webauthn_login.error": "Unable to login with passkey",
"page.integrations.title": "Integrasi",
"page.integration.miniflux_api": "API Miniflux",
"page.integration.miniflux_api_endpoint": "Titik URL API",
@ -206,10 +243,12 @@
"page.offline.title": "Mode Luring",
"page.offline.message": "Anda sedang luring",
"page.offline.refresh_page": "Coba untuk memuat ulang halaman ini",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Tidak ada entri yang dibagikan.",
"alert.no_bookmark": "Tidak ada markah.",
"alert.no_category": "Tidak ada kategori.",
"alert.no_category_entry": "Tidak ada artikel di kategori ini.",
"alert.no_tag_entry": "Tidak ada entri yang cocok dengan tag ini.",
"alert.no_feed_entry": "Tidak ada artikel di umpan ini.",
"alert.no_feed": "Anda tidak memiliki langganan.",
"alert.no_feed_in_category": "Tidak ada langganan untuk kategori ini.",
@ -268,6 +307,7 @@
"form.feed.label.title": "Judul",
"form.feed.label.site_url": "URL Situs",
"form.feed.label.feed_url": "URL Umpan",
"form.feed.label.description": "Deskripsi",
"form.feed.label.category": "Kategori",
"form.feed.label.crawler": "Ambil konten asli",
"form.feed.label.feed_username": "Nama Pengguna Umpan",
@ -282,6 +322,7 @@
"form.feed.label.urlrewrite_rules": "Aturan Tulis Ulang URL",
"form.feed.label.ignore_http_cache": "Abaikan Tembolok HTTP",
"form.feed.label.allow_self_signed_certificates": "Perbolehkan sertifikat web tidak valid atau sertifikasi sendiri",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Ambil via Proksi",
"form.feed.label.disabled": "Jangan perbarui umpan ini",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -366,6 +407,9 @@
"form.integration.nunux_keeper_activate": "Simpan artikel ke Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Titik URL API Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Kunci API Nunux Keeper",
"form.integration.omnivore_activate": "Simpan artikel ke Omnivore",
"form.integration.omnivore_url": "Titik URL API Omnivore",
"form.integration.omnivore_api_key": "Kunci API Omnivore",
"form.integration.espial_activate": "Simpan artikel ke Espial",
"form.integration.espial_endpoint": "Titik URL API Espial",
"form.integration.espial_api_key": "Kunci API Espial",
@ -380,16 +424,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Simpan artikel ke Linkding",
"form.integration.linkding_endpoint": "Titik URL API Linkding",
"form.integration.linkding_api_key": "Kunci API Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Tandai markah sebagai belum dibaca",
"form.integration.linkwarden_activate": "Simpan artikel ke Linkwarden",
"form.integration.linkwarden_endpoint": "Titik URL API Linkwarden",
"form.integration.linkwarden_api_key": "Kunci API Linkwarden",
"form.integration.matrix_bot_activate": "Kirim entri baru ke Matrix",
"form.integration.matrix_bot_user": "Nama Pengguna Matrix",
"form.integration.matrix_bot_password": "Kata Sandi Matrix",
"form.integration.matrix_bot_url": "URL Peladen Matrix",
"form.integration.matrix_bot_chat_id": "ID Ruang Matrix",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Simpan artikel ke Readeck",
"form.integration.readeck_endpoint": "Titik URL API Readeck",
"form.integration.readeck_api_key": "Kunci API Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Kirim hanya URL (alih-alih konten penuh)",
"form.integration.shiori_activate": "Save articles to Shiori",
"form.integration.shiori_endpoint": "Shiori API Endpoint",
"form.integration.shiori_username": "Shiori Username",
@ -400,6 +462,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "Label Kunci API",
"form.submit.loading": "Memuat...",
"form.submit.saving": "Menyimpan...",
@ -407,40 +471,60 @@
"time_elapsed.yesterday": "kemarin",
"time_elapsed.now": "baru saja",
"time_elapsed.minutes": [
"%d menit yang lalu"
"%d menit yang lalu"
],
"time_elapsed.hours": [
"%d jam yang lalu"
"%d jam yang lalu"
],
"time_elapsed.days": [
"%d hari yang lalu"
"%d hari yang lalu"
],
"time_elapsed.weeks": [
"%d pekan yang lalu"
"%d pekan yang lalu"
],
"time_elapsed.months": [
"%d bulan yang lalu"
"%d bulan yang lalu"
],
"time_elapsed.years": [
"%d tahun yang lalu"
"%d tahun yang lalu"
],
"This feed already exists (%s)": "Umpan ini sudah ada (%s)",
"Unable to fetch feed (Status Code = %d)": "Tidak bisa mengambil umpan (Kode Status = %d)",
"Unable to open this link: %v": "Tidak bisa membuka tautan ini: %v",
"Unable to analyze this page: %v": "Tidak bisa menganalisis halaman ini: %v",
"Unable to execute request: %v": "Tidak bisa mengeksekusi permintaan: %v",
"Unable to parse OPML file: %q": "Tidak bisa mengurai berkas OPML: %q",
"Unable to parse RSS feed: %q": "Tidak bisa mengurai umpan RSS: %q",
"Unable to parse Atom feed: %q": "Tidak bisa mengurai umpan Atom: %q",
"Unable to parse JSON feed: %q": "Tidak bisa mengurai umpan JSON: %q",
"Unable to parse RDF feed: %q": "Tidak bisa mengurai umpan RDF: %q",
"Unable to normalize encoding: %q": "Tidak dapat menormalisasi enkode: %q",
"This feed is empty": "Umpan ini kosong",
"This web page is empty": "Halaman web ini kosong",
"Invalid SSL certificate (original error: %q)": "Sertifikat SSL tidak valid (galat: %q)",
"This website is unreachable (original error: %q)": "Situs ini tidak dapat tersambung (galat: %q)",
"Website unreachable, the request timed out after %d seconds": "Situs tidak dapat tersambung, permintaan galat setelah %d detik",
"You are not authorized to access this resource (invalid username/password)": "Anda tidak memiliki izin yang cukup untuk mengakses umpan ini (nama pengguna/kata sandi tidak valid)",
"Unable to fetch this resource (Status Code = %d)": "Tidak bisa mengambil umpan ini (Kode Status = %d)",
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Umpan tidak ditemukan (404), umpan ini tidak ada lagi, periksa URL umpan"
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Kecepatan pemutaran audio/video",
"error.settings_media_playback_rate_range": "Kecepatan pemutaran di luar jangkauan",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Sei sicuro?",
"confirm.question.refresh": "Vuoi forzare l'aggiornamento?",
"confirm.yes": "sì",
@ -18,6 +19,8 @@
"action.home_screen": "Aggiungere alla schermata Home",
"tooltip.keyboard_shortcuts": "Scorciatoia da tastiera: %s",
"tooltip.logged_user": "Autenticato come %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Da leggere",
"menu.starred": "Preferiti",
"menu.history": "Cronologia",
@ -32,6 +35,7 @@
"menu.about": "Informazioni",
"menu.export": "Esporta",
"menu.import": "Importa",
"menu.search": "Cerca",
"menu.create_category": "Aggiungi una categoria",
"menu.mark_page_as_read": "Segna questa pagina come letta",
"menu.mark_all_as_read": "Segna tutti gli articoli come letti",
@ -50,6 +54,7 @@
"menu.shared_entries": "Voci condivise",
"search.label": "Cerca",
"search.placeholder": "Cerca...",
"search.submit": "Search",
"pagination.next": "Successivo",
"pagination.previous": "Precedente",
"entry.status.unread": "Da leggere",
@ -81,11 +86,27 @@
"entry.estimated_reading_time": [
"%d minuto di lettura",
"%d minuti di lettura"
],
],
"entry.tags.label": "Tag:",
"page.shared_entries.title": "Voci condivise",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Da leggere",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Preferiti",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Categorie",
"page.categories.no_feed": "Nessun feed.",
"page.categories.entries": "Articoli",
@ -94,20 +115,28 @@
"C'è %d feed.",
"Ci sono %d feed."
],
"page.categories.unread_counter": "Numero di voci non lette",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "Nuova categoria",
"page.new_user.title": "Nuovo utente",
"page.edit_category.title": "Modifica categoria: %s",
"page.edit_user.title": "Modifica utente: %s",
"page.feeds.title": "Feed",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Ultimo controllo:",
"page.feeds.unread_counter": "Numero di voci non lette",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Numero di voci lette",
"page.feeds.error_count": [
"%d errore",
"%d errori"
],
"page.history.title": "Cronologia",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Importa",
"page.search.title": "Risultati della ricerca",
"page.about.title": "Informazioni",
@ -149,6 +178,8 @@
"page.keyboard_shortcuts.go_to_feed": "Mostra il feed",
"page.keyboard_shortcuts.go_to_previous_page": "Mostra la pagina precedente",
"page.keyboard_shortcuts.go_to_next_page": "Mostra la pagina successiva",
"page.keyboard_shortcuts.go_to_bottom_item": "Vai all'elemento in fondo",
"page.keyboard_shortcuts.go_to_top_item": "Vai all'elemento principale",
"page.keyboard_shortcuts.open_item": "Apri l'articolo selezionato",
"page.keyboard_shortcuts.open_original": "Apri la pagina web originale",
"page.keyboard_shortcuts.open_original_same_window": "Apri il link originale nella scheda corrente",
@ -179,9 +210,22 @@
"page.settings.unlink_google_account": "Scollega il mio account Google",
"page.settings.link_oidc_account": "Collega il mio account OpenID Connect",
"page.settings.unlink_oidc_account": "Scollega il mio account OpenID Connect",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Registra la chiave di accesso",
"page.settings.webauthn.register.error": "Impossibile registrare la passkey",
"page.settings.webauthn.delete": [
"Rimuovi %d passkey",
"Rimuovi %d passkey"
],
"page.login.title": "Accedi",
"page.login.google_signin": "Accedi tramite Google",
"page.login.oidc_signin": "Accedi tramite OpenID Connect",
"page.login.webauthn_login": "Accedi con passkey",
"page.login.webauthn_login.error": "Impossibile accedere con passkey",
"page.integrations.title": "Integrazioni",
"page.integration.miniflux_api": "API di Miniflux",
"page.integration.miniflux_api_endpoint": "Endpoint dell'API di Miniflux",
@ -209,10 +253,12 @@
"page.offline.title": "Modalità offline",
"page.offline.message": "Sei offline",
"page.offline.refresh_page": "Prova ad aggiornare la pagina",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Non ci sono voci condivise.",
"alert.no_bookmark": "Nessun preferito disponibile.",
"alert.no_category": "Nessuna categoria disponibile.",
"alert.no_category_entry": "Questa categoria non contiene alcun articolo.",
"alert.no_tag_entry": "Non ci sono voci corrispondenti a questo tag.",
"alert.no_feed_entry": "Questo feed non contiene alcun articolo.",
"alert.no_feed": "Nessun feed disponibile.",
"alert.no_feed_in_category": "Non esiste un abbonamento per questa categoria.",
@ -271,6 +317,7 @@
"form.feed.label.title": "Titolo",
"form.feed.label.site_url": "URL del sito",
"form.feed.label.feed_url": "URL del feed",
"form.feed.label.description": "Descrizione",
"form.feed.label.category": "Categoria",
"form.feed.label.crawler": "Scarica il contenuto integrale",
"form.feed.label.feed_username": "Nome utente del feed",
@ -285,6 +332,7 @@
"form.feed.label.urlrewrite_rules": "Regole di riscrittura URL",
"form.feed.label.ignore_http_cache": "Ignora cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Consenti certificati autofirmati o non validi",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Recuperare tramite proxy",
"form.feed.label.disabled": "Non aggiornare questo feed",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Salva gli articoli su Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Endpoint dell'API di Nunux Keeper",
"form.integration.nunux_keeper_api_key": "API key dell'account Nunux Keeper",
"form.integration.omnivore_activate": "Salva gli articoli su Omnivore",
"form.integration.omnivore_url": "Endpoint dell'API di Omnivore",
"form.integration.omnivore_api_key": "API key dell'account Omnivore",
"form.integration.espial_activate": "Salva gli articoli su Espial",
"form.integration.espial_endpoint": "Endpoint dell'API di Espial",
"form.integration.espial_api_key": "API key dell'account Espial",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Salva gli articoli su LinkAce",
"form.integration.linkace_endpoint": "Endpoint dell'API di LinkAce",
"form.integration.linkace_api_key": "API key dell'account LinkAce",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Rendi i link privati",
"form.integration.linkace_check_disabled": "Disabilita i controlli",
"form.integration.linkding_activate": "Salva gli articoli su Linkding",
"form.integration.linkding_endpoint": "Endpoint dell'API di Linkding",
"form.integration.linkding_api_key": "API key dell'account Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Segna i preferiti come non letti",
"form.integration.linkwarden_activate": "Salva gli articoli su Linkwarden",
"form.integration.linkwarden_endpoint": "Endpoint dell'API di Linkwarden",
"form.integration.linkwarden_api_key": "API key dell'account Linkwarden",
"form.integration.matrix_bot_activate": "Trasferimento di nuovi articoli a Matrix",
"form.integration.matrix_bot_user": "Nome utente per Matrix",
"form.integration.matrix_bot_password": "Password per l'utente Matrix",
"form.integration.matrix_bot_url": "URL del server Matrix",
"form.integration.matrix_bot_chat_id": "ID della stanza Matrix",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Salva gli articoli su Readeck",
"form.integration.readeck_endpoint": "Endpoint dell'API di Readeck",
"form.integration.readeck_api_key": "API key dell'account Readeck",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Invia solo URL (invece del contenuto completo)",
"form.integration.shiori_activate": "Salva gli articoli su Shiori",
"form.integration.shiori_endpoint": "Endpoint dell'API di Shiori",
"form.integration.shiori_username": "Nome utente dell'account Shiori",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "Etichetta chiave API",
"form.submit.loading": "Caricamento in corso...",
"form.submit.saving": "Salvataggio in corso...",
@ -432,5 +503,45 @@
"time_elapsed.years": [
"%d anno fa",
"%d anni fa"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Velocità di riproduzione dell'audio/video",
"error.settings_media_playback_rate_range": "La velocità di riproduzione non rientra nell'intervallo",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "よろしいですか?",
"confirm.question.refresh": "強制的に更新しますか?",
"confirm.yes": "はい",
@ -18,6 +19,8 @@
"action.home_screen": "ホームスクリーンに追加",
"tooltip.keyboard_shortcuts": "キーボードショートカット: %s",
"tooltip.logged_user": "%s としてログイン中",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "未読",
"menu.starred": "星付き",
"menu.history": "履歴",
@ -32,6 +35,7 @@
"menu.about": "ソフトウェア情報",
"menu.export": "エクスポート",
"menu.import": "インポート",
"menu.search": "検索",
"menu.create_category": "カテゴリを作成",
"menu.mark_page_as_read": "このページを既読にする",
"menu.mark_all_as_read": "すべて既読にする",
@ -50,6 +54,7 @@
"menu.shared_entries": "共有エントリ",
"search.label": "検索",
"search.placeholder": "…を検索",
"search.submit": "Search",
"pagination.next": "次",
"pagination.previous": "前",
"entry.status.unread": "未読にする",
@ -79,35 +84,50 @@
"entry.shared_entry.title": "公開リンクを開く",
"entry.shared_entry.label": "共有する",
"entry.estimated_reading_time": [
"%d 分で読めます",
"%d 分で読めます"
],
"entry.tags.label": "タグ:",
"page.shared_entries.title": "共有エントリ",
"page.shared_entries_count": [
"%d shared entry"
],
"page.unread.title": "未読",
"page.unread_entry_count": [
"%d unread entry"
],
"page.total_entry_count": [
"%d entry in total"
],
"page.starred.title": "星付き",
"page.starred_entry_count": [
"%d starred entry"
],
"page.categories.title": "カテゴリ",
"page.categories.no_feed": "フィードはありません。",
"page.categories.entries": "記事一覧",
"page.categories.feeds": "フィード一覧",
"page.categories.feed_count": [
"%d 件のフィードがあります。",
"%d 件のフィードがあります。"
],
"page.categories.unread_counter": "未読記事の数",
"page.categories_count": [
"%d category"
],
"page.new_category.title": "新規カテゴリ",
"page.new_user.title": "新規ユーザー",
"page.edit_category.title": "カテゴリを編集: %s",
"page.edit_user.title": "ユーザーを編集: %s",
"page.feeds.title": "フィード一覧",
"page.category_label": "Category: %s",
"page.feeds.last_check": "最終チェック:",
"page.feeds.unread_counter": "未読記事の数",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "既読記事の数",
"page.feeds.error_count": [
"%d 個のエラー",
"%d 個のエラー"
],
"page.history.title": "履歴",
"page.read_entry_count": [
"%d read entry"
],
"page.import.title": "インポート",
"page.search.title": "検索結果",
"page.about.title": "ソフトウェア情報",
@ -149,6 +169,8 @@
"page.keyboard_shortcuts.go_to_feed": "フィード",
"page.keyboard_shortcuts.go_to_previous_page": "前のページ",
"page.keyboard_shortcuts.go_to_next_page": "次のページ",
"page.keyboard_shortcuts.go_to_bottom_item": "一番下の項目に移動",
"page.keyboard_shortcuts.go_to_top_item": "先頭の項目に移動",
"page.keyboard_shortcuts.open_item": "選択されたアイテムを開く",
"page.keyboard_shortcuts.open_original": "オリジナルのリンクを開く",
"page.keyboard_shortcuts.open_original_same_window": "現在のタブでオリジナルのリンクを開く",
@ -179,9 +201,21 @@
"page.settings.unlink_google_account": "Google アカウントと接続を解除する",
"page.settings.link_oidc_account": "OpenID Connect アカウントと接続する",
"page.settings.unlink_oidc_account": "OpenID Connect アカウントと接続を解除する",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "パスキーを登録する",
"page.settings.webauthn.register.error": "パスキーを登録できません",
"page.settings.webauthn.delete": [
"%d 個のパスキーを削除"
],
"page.login.title": "ログイン",
"page.login.google_signin": "Google アカウントでログイン",
"page.login.oidc_signin": "OpenID Connect アカウントでログイン",
"page.login.webauthn_login": "パスキーでログイン",
"page.login.webauthn_login.error": "パスキーでログインできない",
"page.integrations.title": "連携",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Endpoint",
@ -209,10 +243,12 @@
"page.offline.title": "オフラインモード",
"page.offline.message": "オフラインです",
"page.offline.refresh_page": "ページを更新してみてください",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "共有エントリはありません。",
"alert.no_bookmark": "現在星付きはありません。",
"alert.no_category": "カテゴリが存在しません。",
"alert.no_category_entry": "このカテゴリには記事がありません。",
"alert.no_tag_entry": "このタグに一致するエントリーはありません。",
"alert.no_feed_entry": "このフィードには記事がありません。",
"alert.no_feed": "何も購読していません。",
"alert.no_feed_in_category": "このカテゴリには購読中のフィードがありません。",
@ -271,6 +307,7 @@
"form.feed.label.title": "タイトル",
"form.feed.label.site_url": "サイト URL",
"form.feed.label.feed_url": "フィード URL",
"form.feed.label.description": "説明",
"form.feed.label.category": "カテゴリ",
"form.feed.label.crawler": "オリジナルの内容を取得",
"form.feed.label.feed_username": "フィードのユーザー名",
@ -285,6 +322,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "HTTPキャッシュを無視",
"form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "プロキシ経由で取得",
"form.feed.label.disabled": "このフィードを更新しない",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +407,9 @@
"form.integration.nunux_keeper_activate": "Nunux Keeper に記事を保存する",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper の API Endpoint",
"form.integration.nunux_keeper_api_key": "Nunux Keeper の API key",
"form.integration.omnivore_activate": "Omnivore に記事を保存する",
"form.integration.omnivore_url": "Omnivore の API Endpoint",
"form.integration.omnivore_api_key": "Omnivore の API key",
"form.integration.espial_activate": "Espial に記事を保存する",
"form.integration.espial_endpoint": "Espial の API Endpoint",
"form.integration.espial_api_key": "Espial の API key",
@ -383,16 +424,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Linkding に記事を保存する",
"form.integration.linkding_endpoint": "Linkding の API Endpoint",
"form.integration.linkding_api_key": "Linkding の API key",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "ブックマークを未読にする",
"form.integration.linkwarden_activate": "Linkwarden に記事を保存する",
"form.integration.linkwarden_endpoint": "Linkwarden の API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden の API key",
"form.integration.matrix_bot_activate": "新しい記事をMatrixに転送する",
"form.integration.matrix_bot_user": "Matrixのユーザー名",
"form.integration.matrix_bot_password": "Matrixユーザ用パスワード",
"form.integration.matrix_bot_url": "MatrixサーバーのURL",
"form.integration.matrix_bot_chat_id": "MatrixルームのID",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Readeck に記事を保存する",
"form.integration.readeck_endpoint": "Readeck の API Endpoint",
"form.integration.readeck_api_key": "Readeck の API key",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "URL のみを送信 (完全なコンテンツではなく)",
"form.integration.shiori_activate": "Shiori に記事を保存する",
"form.integration.shiori_endpoint": "Shiori の API Endpoint",
"form.integration.shiori_username": "Shiori の ユーザー名",
@ -403,6 +462,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "API キーラベル",
"form.submit.loading": "読み込み中…",
"form.submit.saving": "保存中…",
@ -410,27 +471,60 @@
"time_elapsed.yesterday": "昨日",
"time_elapsed.now": "今",
"time_elapsed.minutes": [
"%d 分前",
"%d 分前"
],
"time_elapsed.hours": [
"%d 時間前",
"%d 時間前"
],
"time_elapsed.days": [
"%d 日前",
"%d 日前"
],
"time_elapsed.weeks": [
"%d 週間前",
"%d 週間前"
],
"time_elapsed.months": [
"%d か月前",
"%d か月前"
],
"time_elapsed.years": [
"%d 年前",
"%d 年前"
]
],
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "オーディオ/ビデオの再生速度",
"error.settings_media_playback_rate_range": "再生速度が範囲外",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

View File

@ -1,4 +1,5 @@
{
"skip_to_content": "Skip to content",
"confirm.question": "Weet je het zeker?",
"confirm.question.refresh": "Wil je een gedwongen vernieuwing uitvoeren?",
"confirm.yes": "ja",
@ -18,6 +19,8 @@
"action.home_screen": "Toevoegen aan startscherm",
"tooltip.keyboard_shortcuts": "Sneltoets: %s",
"tooltip.logged_user": "Ingelogd als %s",
"menu.title": "Menu",
"menu.home_page": "Home page",
"menu.unread": "Ongelezen",
"menu.starred": "Favorieten",
"menu.history": "Geschiedenis",
@ -32,6 +35,7 @@
"menu.about": "Over",
"menu.export": "Exporteren",
"menu.import": "Importeren",
"menu.search": "Zoeken",
"menu.create_category": "Categorie toevoegen",
"menu.mark_page_as_read": "Markeer deze pagina als gelezen",
"menu.mark_all_as_read": "Markeer alle items als gelezen",
@ -50,6 +54,7 @@
"menu.shared_entries": "Gedeelde vermeldingen",
"search.label": "Zoeken",
"search.placeholder": "Zoeken...",
"search.submit": "Search",
"pagination.next": "Volgende",
"pagination.previous": "Vorige",
"entry.status.unread": "Ongelezen",
@ -84,8 +89,24 @@
],
"entry.tags.label": "Labels:",
"page.shared_entries.title": "Gedeelde vermeldingen",
"page.shared_entries_count": [
"%d shared entry",
"%d shared entries"
],
"page.unread.title": "Ongelezen",
"page.unread_entry_count": [
"%d unread entry",
"%d unread entries"
],
"page.total_entry_count": [
"%d entry in total",
"%d entries in total"
],
"page.starred.title": "Favorieten",
"page.starred_entry_count": [
"%d starred entry",
"%d starred entries"
],
"page.categories.title": "Categorieën",
"page.categories.no_feed": "Geen feeds.",
"page.categories.entries": "Lidwoord",
@ -94,20 +115,28 @@
"Er is %d feed.",
"Er zijn %d feeds."
],
"page.categories.unread_counter": "Aantal ongelezen vermeldingen",
"page.categories_count": [
"%d category",
"%d categories"
],
"page.new_category.title": "Nieuwe categorie",
"page.new_user.title": "Nieuwe gebruiker",
"page.edit_category.title": "Bewerken van categorie: %s",
"page.edit_user.title": "Bewerk gebruiker: %s",
"page.feeds.title": "Feeds",
"page.category_label": "Category: %s",
"page.feeds.last_check": "Laatste update:",
"page.feeds.unread_counter": "Aantal ongelezen vermeldingen",
"page.feeds.next_check": "Next check:",
"page.feeds.read_counter": "Aantal gelezen vermeldingen",
"page.feeds.error_count": [
"%d error",
"%d errors"
],
"page.history.title": "Geschiedenis",
"page.read_entry_count": [
"%d read entry",
"%d read entries"
],
"page.import.title": "Importeren",
"page.login.title": "Inloggen",
"page.search.title": "Zoekresultaten",
@ -150,6 +179,8 @@
"page.keyboard_shortcuts.go_to_feed": "Ga naar feed",
"page.keyboard_shortcuts.go_to_previous_page": "Vorige pagina",
"page.keyboard_shortcuts.go_to_next_page": "Volgende pagina",
"page.keyboard_shortcuts.go_to_bottom_item": "Ga naar het onderste item",
"page.keyboard_shortcuts.go_to_top_item": "Ga naar het bovenste item",
"page.keyboard_shortcuts.open_item": "Open geselecteerde link",
"page.keyboard_shortcuts.open_original": "Open originele link",
"page.keyboard_shortcuts.open_original_same_window": "Oorspronkelijke koppeling op huidig tabblad openen",
@ -180,7 +211,20 @@
"page.settings.unlink_google_account": "Ontkoppel mijn Google-account",
"page.settings.link_oidc_account": "Koppel mijn OpenID Connect-account",
"page.settings.unlink_oidc_account": "Ontkoppel mijn OpenID Connect-account",
"page.settings.webauthn.passkeys": "Passkeys",
"page.settings.webauthn.actions": "Actions",
"page.settings.webauthn.passkey_name": "Passkey Name",
"page.settings.webauthn.added_on": "Added On",
"page.settings.webauthn.last_seen_on": "Last Used",
"page.settings.webauthn.register": "Wachtwoord registreren",
"page.settings.webauthn.register.error": "Kan wachtwoord niet registreren",
"page.settings.webauthn.delete": [
"Verwijder %d wachtwoord",
"Verwijder %d wachtwoordsleutels"
],
"page.login.oidc_signin": "Inloggen via OpenID Connect",
"page.login.webauthn_login": "Inloggen met wachtwoord",
"page.login.webauthn_login.error": "Kan niet inloggen met wachtwoord",
"page.login.google_signin": "Inloggen via Google",
"page.integrations.title": "Integraties",
"page.integration.miniflux_api": "Miniflux API",
@ -209,10 +253,12 @@
"page.offline.title": "Offline modus",
"page.offline.message": "Je bent offline",
"page.offline.refresh_page": "Probeer de pagina te vernieuwen",
"page.webauthn_rename.title": "Rename Passkey",
"alert.no_shared_entry": "Er is geen gedeelde toegang.",
"alert.no_bookmark": "Er zijn op dit moment geen favorieten.",
"alert.no_category": "Er zijn geen categorieën.",
"alert.no_category_entry": "Deze categorie bevat geen feeds.",
"alert.no_tag_entry": "Er zijn geen items die overeenkomen met deze tag.",
"alert.no_feed_entry": "Er zijn geen artikelen in deze feed.",
"alert.no_feed": "Je hebt nog geen feeds geabboneerd staan.",
"alert.no_feed_in_category": "Er is geen abonnement voor deze categorie.",
@ -271,6 +317,7 @@
"form.feed.label.title": "Naam",
"form.feed.label.site_url": "Website URL",
"form.feed.label.feed_url": "Feed URL",
"form.feed.label.description": "Beschrijving",
"form.feed.label.category": "Categorie",
"form.feed.label.crawler": "Download originele content",
"form.feed.label.feed_username": "Feed-gebruikersnaam",
@ -285,6 +332,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "Negeer HTTP-cache",
"form.feed.label.allow_self_signed_certificates": "Sta zelfondertekende of ongeldige certificaten toe",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Ophalen via proxy",
"form.feed.label.disabled": "Vernieuw deze feed niet",
"form.feed.label.no_media_player": "No media player (audio/video)",
@ -369,6 +417,9 @@
"form.integration.nunux_keeper_activate": "Opslaan naar Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper URL",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-sleutel",
"form.integration.omnivore_activate": "Opslaan naar Omnivore",
"form.integration.omnivore_url": "Omnivore URL",
"form.integration.omnivore_api_key": "Omnivore API-sleutel",
"form.integration.espial_activate": "Opslaan naar Espial",
"form.integration.espial_endpoint": "Espial URL",
"form.integration.espial_api_key": "Espial API-sleutel",
@ -383,16 +434,34 @@
"form.integration.telegram_bot_disable_web_page_preview": "Disable web page preview",
"form.integration.telegram_bot_disable_notification": "Disable notification",
"form.integration.telegram_bot_disable_buttons": "Disable buttons",
"form.integration.linkace_activate": "Save entries to LinkAce",
"form.integration.linkace_endpoint": "LinkAce API Endpoint",
"form.integration.linkace_api_key": "LinkAce API key",
"form.integration.linkace_tags": "LinkAce Tags",
"form.integration.linkace_is_private": "Mark link as private",
"form.integration.linkace_check_disabled": "Disable link check",
"form.integration.linkding_activate": "Opslaan naar Linkding",
"form.integration.linkding_endpoint": "Linkding URL",
"form.integration.linkding_api_key": "Linkding API-sleutel",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Markeer bookmark als gelezen",
"form.integration.linkwarden_activate": "Opslaan naar Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden URL",
"form.integration.linkwarden_api_key": "Linkwarden API-sleutel",
"form.integration.matrix_bot_activate": "Nieuwe artikelen overbrengen naar Matrix",
"form.integration.matrix_bot_user": "Gebruikersnaam voor Matrix",
"form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker",
"form.integration.matrix_bot_url": "URL van de Matrix-server",
"form.integration.matrix_bot_chat_id": "ID van Matrix-kamer",
"form.integration.raindrop_activate": "Save entries to Raindrop",
"form.integration.raindrop_token": "(Test) Token",
"form.integration.raindrop_collection_id": "Collection ID",
"form.integration.raindrop_tags": "Tags (comma-separated)",
"form.integration.readeck_activate": "Opslaan naar Readeck",
"form.integration.readeck_endpoint": "Readeck URL",
"form.integration.readeck_api_key": "Readeck API-sleutel",
"form.integration.readeck_labels": "Readeck Labels",
"form.integration.readeck_only_url": "Alleen URL verzenden (in plaats van volledige inhoud)",
"form.integration.shiori_activate": "Opslaan naar Shiori",
"form.integration.shiori_endpoint": "Shiori URL",
"form.integration.shiori_username": "Shiori gebruikersnaam",
@ -403,6 +472,8 @@
"form.integration.webhook_activate": "Enable Webhook",
"form.integration.webhook_url": "Webhook URL",
"form.integration.webhook_secret": "Webhook Secret",
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.api_key.label.description": "API-sleutellabel",
"form.submit.loading": "Laden...",
"form.submit.saving": "Opslaag...",
@ -433,21 +504,44 @@
"%d jaar geleden",
"%d jaar geleden"
],
"This feed already exists (%s)": "Deze feed bestaat al (%s)",
"Unable to fetch feed (Status Code = %d)": "Kon feed niet updaten (statuscode = %d)",
"Unable to open this link: %v": "Kon link niet volgen: %v",
"Unable to analyze this page: %v": "Kon pagina niet analyseren: %v",
"Unable to execute request: %v": "Kon request niet uitvoeren: %v",
"Unable to parse OPML file: %q": "Kon OPML niet parsen: %q",
"Unable to parse RSS feed: %q": "Kon RSS-feed niet parsen: %q",
"Unable to parse Atom feed: %q": "Kon Atom-feed niet parsen: %q",
"Unable to parse JSON feed: %q": "Kon JSON-feed niet parsen: %q",
"Unable to parse RDF feed: %q": "Kon RDF-feed niet parsen: %q",
"Unable to normalize encoding: %q": "Kon encoding niet normaliseren: %q",
"Unable to create this category.": "Kon categorie niet aanmaken.",
"Category not found for this user": "Categorie niet gevonden voor deze gebruiker",
"This web page is empty": "Deze webpagina is leeg",
"Invalid SSL certificate (original error: %q)": "Ongeldig SSL-certificaat (originele error: %q)",
"This website is unreachable (original error: %q)": "Deze website is onbereikbaar (originele error: %q)",
"Website unreachable, the request timed out after %d seconds": "Website onbereikbaar, de request gaf een timeout na %d seconden"
"alert.too_many_feeds_refresh": [
"You have triggered too many feed refreshes. Please wait %d minute before trying again.",
"You have triggered too many feed refreshes. Please wait %d minutes before trying again."
],
"alert.background_feed_refresh": "All feeds are being refreshed in the background. You can continue to use Miniflux while this process is running.",
"error.http_response_too_large": "The HTTP response is too large. You could increase the HTTP response size limit in the global settings (requires a server restart).",
"error.http_body_read": "Unable to read the HTTP body: %v.",
"error.http_empty_response_body": "The HTTP response body is empty.",
"error.http_empty_response": "The HTTP response is empty. Perhaps, this website is using a bot protection mechanism?",
"error.tls_error": "TLS error: %q. You could disable TLS verification in the feed settings if you would like.",
"error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
"error.network_timeout": "This website is too slow and the request timed out: %v",
"error.http_client_error": "HTTP client error: %v.",
"error.http_not_authorized": "Access to this website is not authorized. It could be a bad username or password.",
"error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
"error.http_forbidden": "Access to this website is forbidden. Perhaps, this website has a bot protection mechanism?",
"error.http_resource_not_found": "The requested resource is not found. Please, verify the URL.",
"error.http_internal_server_error": "The website is not available at the moment due to a server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_bad_gateway": "The website is not available at the moment due to a bad gateway error. The problem is not on Miniflux side. Please, try again later.",
"error.http_service_unavailable": "The website is not available at the moment due to an internal server error. The problem is not on Miniflux side. Please, try again later.",
"error.http_gateway_timeout": "The website is not available at the moment due to a gateway timeout error. The problem is not on Miniflux side. Please, try again later.",
"error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
"error.database_error": "Database error: %v.",
"error.category_not_found": "This category does not exist or does not belong to this user.",
"error.duplicated_feed": "This feed already exists.",
"error.unable_to_parse_feed": "Unable to parse this feed: %v.",
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v.",
"form.prefs.label.media_playback_rate": "Afspeelsnelheid van de audio/video",
"error.settings_media_playback_rate_range": "Afspeelsnelheid is buiten bereik",
"enclosure_media_controls.seek" : "Seek:",
"enclosure_media_controls.seek.title" : "Seek %s seconds",
"enclosure_media_controls.speed" : "Speed:",
"enclosure_media_controls.speed.faster" : "Faster",
"enclosure_media_controls.speed.faster.title" : "Faster by %sx",
"enclosure_media_controls.speed.slower" : "Slower",
"enclosure_media_controls.speed.slower.title" : "Slower by %sx",
"enclosure_media_controls.speed.reset" : "Reset",
"enclosure_media_controls.speed.reset.title" : "Reset speed to 1x"
}

Some files were not shown because too many files have changed in this diff Show More