Merge branch 'develop' into feature/push-subject-for-dm
authorMark Felder <feld@FreeBSD.org>
Fri, 18 Oct 2019 22:56:15 +0000 (17:56 -0500)
committerMark Felder <feld@FreeBSD.org>
Fri, 18 Oct 2019 22:56:15 +0000 (17:56 -0500)
88 files changed:
.gitlab-ci.yml
CHANGELOG.md
benchmarks/load_testing/fetcher.ex [new file with mode: 0644]
benchmarks/load_testing/generator.ex [new file with mode: 0644]
benchmarks/load_testing/helper.ex [new file with mode: 0644]
benchmarks/mix/tasks/pleroma/load_testing.ex [new file with mode: 0644]
config/benchmark.exs [new file with mode: 0644]
config/config.exs
config/description.exs
config/releases.exs
docs/API/admin_api.md
lib/mix/tasks/pleroma/database.ex
lib/mix/tasks/pleroma/relay.ex
lib/pleroma/application.ex
lib/pleroma/conversation/participation.ex
lib/pleroma/moderation_log.ex
lib/pleroma/object.ex
lib/pleroma/object/containment.ex
lib/pleroma/object/fetcher.ex
lib/pleroma/upload.ex
lib/pleroma/user.ex
lib/pleroma/user/info.ex
lib/pleroma/user/search.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/publisher.ex
lib/pleroma/web/activity_pub/relay.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/views/account_view.ex
lib/pleroma/web/federator/federator.ex
lib/pleroma/web/federator/publisher.ex
lib/pleroma/web/mastodon_api/websocket_handler.ex
lib/pleroma/web/ostatus/activity_representer.ex [deleted file]
lib/pleroma/web/ostatus/feed_representer.ex [deleted file]
lib/pleroma/web/ostatus/handlers/delete_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/follow_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/note_handler.ex [deleted file]
lib/pleroma/web/ostatus/handlers/unfollow_handler.ex [deleted file]
lib/pleroma/web/ostatus/ostatus.ex [deleted file]
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/ostatus/user_representer.ex [deleted file]
lib/pleroma/web/router.ex
lib/pleroma/web/salmon/salmon.ex [deleted file]
lib/pleroma/web/streamer/streamer.ex
lib/pleroma/web/templates/feed/feed/feed.xml.eex
lib/pleroma/web/web_finger/web_finger.ex
lib/pleroma/web/websub/websub.ex [deleted file]
lib/pleroma/web/websub/websub_client_subscription.ex [deleted file]
lib/pleroma/web/websub/websub_controller.ex [deleted file]
lib/pleroma/web/websub/websub_server_subscription.ex [deleted file]
lib/pleroma/workers/receiver_worker.ex
lib/pleroma/workers/subscriber_worker.ex [deleted file]
mix.exs
priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs [new file with mode: 0644]
priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs
rel/files/bin/pleroma_ctl
test/conversation/participation_test.exs
test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json [new file with mode: 0644]
test/fixtures/tesla_mock/moonman@shitposter.club.json [new file with mode: 0644]
test/moderation_log_test.exs
test/object/containment_test.exs
test/object/fetcher_test.exs
test/safe_jsonb_set_test.exs [new file with mode: 0644]
test/signature_test.exs
test/support/factory.ex
test/support/http_request_mock.ex
test/user_search_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/relay_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/federator_test.exs
test/web/mastodon_api/controllers/conversation_controller_test.exs
test/web/mastodon_api/controllers/search_controller_test.exs
test/web/mastodon_api/controllers/timeline_controller_test.exs
test/web/mastodon_api/views/account_view_test.exs
test/web/mastodon_api/views/status_view_test.exs
test/web/ostatus/activity_representer_test.exs [deleted file]
test/web/ostatus/feed_representer_test.exs [deleted file]
test/web/ostatus/incoming_documents/delete_handling_test.exs [deleted file]
test/web/ostatus/ostatus_controller_test.exs
test/web/ostatus/ostatus_test.exs [deleted file]
test/web/ostatus/user_representer_test.exs [deleted file]
test/web/salmon/salmon_test.exs [deleted file]
test/web/web_finger/web_finger_test.exs
test/web/websub/websub_controller_test.exs [deleted file]
test/web/websub/websub_test.exs [deleted file]

index 36137b38e09b73e7bd47a0880c59680d230af436..04af8c186283fe701ad982ea27d5c8e100c92e0d 100644 (file)
@@ -15,6 +15,7 @@ cache:
 stages:
   - build
   - test
+  - benchmark
   - deploy
   - release
 
@@ -28,6 +29,36 @@ build:
   - mix deps.get
   - mix compile --force
 
+docs-build:
+  stage: build
+  only:
+  - master@pleroma/pleroma
+  - develop@pleroma/pleroma
+  variables:
+    MIX_ENV: dev
+    PLEROMA_BUILD_ENV: prod
+  script:
+    - mix deps.get
+    - mix compile
+    - mix docs
+  artifacts:
+    paths:
+      - priv/static/doc
+
+benchmark:
+  stage: benchmark
+  variables:
+    MIX_ENV: benchmark
+  services:
+  - name: lainsoykaf/postgres-with-rum
+    alias: postgres
+    command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+  script:
+    - mix deps.get
+    - mix ecto.create
+    - mix ecto.migrate
+    - mix pleroma.load_testing
+
 unit-testing:
   stage: test
   services:
index e3ccfa4ea696cf33512175bd9a3d84709b964eb4..d516080b44f43db503e08ef15a57a9f3a5e09bc6 100644 (file)
@@ -4,8 +4,39 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
+### Removed
+- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
+- **Breaking**: OStatus protocol support
+
+### Changed
+- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
+- Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings)
+- Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler
+- Enabled `:instance, extended_nickname_format` in the default config
+- Add `rel="ugc"` to all links in statuses, to prevent SEO spam
+- Extract RSS functionality from OStatus
+- MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
+<details>
+  <summary>API Changes</summary>
+
+- **Breaking:** Admin API: Return link alongside with token on password reset
+- **Breaking:** `/api/pleroma/admin/users/invite_token` now uses `POST`, changed accepted params and returns full invite in json instead of only token string.
+- Admin API: Return `total` when querying for reports
+- Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`)
+- Admin API: Return link alongside with token on password reset
+- Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
+- Mastodon API: `pleroma.thread_muted` to the Status entity
+- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
+</details>
+
 ### Added
 - Refreshing poll results for remote polls
+- Authentication: Added rate limit for password-authorized actions / login existence checks
+- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
+- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
+<details>
+  <summary>API Changes</summary>
+
 - Job queue stats to the healthcheck page
 - Admin API: Add ability to require password reset
 - Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
@@ -14,10 +45,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Add `upload_limit`, `avatar_upload_limit`, `background_upload_limit`, and `banner_upload_limit` to `/api/v1/instance`
 - Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
 - OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
-- Authentication: Added rate limit for password-authorized actions / login existence checks
 - Metadata Link: Atom syndication Feed
-- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
 - Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
+- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
+- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
+- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
 
 ### Changed
 - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
@@ -30,15 +62,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities
 - OStatus: Extract RSS functionality
 - Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`)
+- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
+</details>
 
 ### Fixed
+- Report emails now include functional links to profiles of remote user accounts
+<details>
+  <summary>API Changes</summary>
+
 - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
 - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
-- Added `:instance, extended_nickname_format` setting to the default config
-- Report emails now include functional links to profiles of remote user accounts
+</details>
+
+## [1.1.2] - 2019-10-18
+### Fixed
+- `pleroma_ctl` trying to connect to a running instance when generating the config, which of course doesn't exist.
 
-## [1.1.0] - 2019-??-??
-**Breaking:** The stable branch has been changed from `master` to `stable`, `master` now points to `release/1.0`
+## [1.1.1] - 2019-10-18
+### Fixed
+- One of the migrations between 1.0.0 and 1.1.0 wiping user info of the relay user because of unexpected behavior of postgresql's `jsonb_set`, resulting in inability to post in the default configuration. If you were affected, please run the following query in postgres console, the relay user will be recreated automatically:
+```
+delete from users where ap_id = 'https://your.instance.hostname/relay';
+```
+- Bad user search matches
+
+## [1.1.0] - 2019-10-14
+**Breaking:** The stable branch has been changed from `master` to `stable`. If you want to keep using 1.0, the `release/1.0` branch will receive security updates for 6 months after 1.1 release.
+
+**OTP Note:** `pleroma_ctl` in 1.0 defaults to `master` and doesn't support specifying arbitrary branches, making `./pleroma_ctl update` fail. To fix this, fetch a version of `pleroma_ctl` from 1.1 using the command below and proceed with the update normally:
+```
+curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/develop/rel/files/bin/pleroma_ctl'
+```
 ### Security
 - Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
 
@@ -46,16 +100,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking:** GNU Social API with Qvitter extensions support
 - Emoji: Remove longfox emojis.
 - Remove `Reply-To` header from report emails for admins.
+- ActivityPub: The `/objects/:uuid/likes` endpoint.
 
 ### Changed
 - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
 - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
 - **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
-- **Breaking:** `/api/pleroma/admin/users/invite_token` now uses `POST`, changed accepted params and returns full invite in json instead of only token string.
 - Configuration: added `config/description.exs`, from which `docs/config.md` is generated
 - Configuration: OpenGraph and TwitterCard providers enabled by default
 - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
-- Mastodon API: `pleroma.thread_muted` key in the Status entity
 - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
 - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
 - NodeInfo: Return `mailerEnabled` in `metadata`
@@ -64,7 +117,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
 - Improve digest email template
 – Pagination: (optional) return `total` alongside with `items` when paginating
-- Add `rel="ugc"` to all links in statuses, to prevent SEO spam
+- The `Pleroma.FlakeId` module has been replaced with the `flake_id` library.
 
 ### Fixed
 - Following from Osada
@@ -75,21 +128,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Misskey's endless polls being unable to render
 - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
 - Mastodon API: Notifications endpoint crashing if one notification failed to render
+- Mastodon API: `exclude_replies` is correctly handled again.
 - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
 - Mastodon API, streaming: Fix filtering of notifications based on blocks/mutes/thread mutes
-- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
-- Existing user id not being preserved on insert conflict
+- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
+- Mastodon API: Ensure the `account` field is not empty when rendering Notification entities.
+- Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
+- Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs
 - Rich Media: Parser failing when no TTL can be found by image TTL setters
 - Rich Media: The crawled URL is now spliced into the rich media data.
 - ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
-- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
-- Report email not being sent to admins when the reporter is a remote user
-- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
+- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
 - ActivityPub: Deactivated user deletion
 - ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user
 - MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled
-- Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs
-- Mastodon API: `exclude_replies` is correctly handled again.
+- ActivityPub: Correct addressing of Undo.
+- ActivityPub: Correct addressing of profile update activities.
+- ActivityPub: Polls are now refreshed when necessary.
+- Report emails now include functional links to profiles of remote user accounts
+- Existing user id not being preserved on insert conflict
+- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
+- Report email not being sent to admins when the reporter is a remote user
+- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
 
 ### Added
 - Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically.
@@ -103,6 +163,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
 - Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
 - Mastodon API, extension: Ability to reset avatar, profile banner, and background
+- Mastodon API: Add support for `fields_attributes` API parameter (setting custom fields)
 - Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196>
 - Mastodon API: Add support for muting/unmuting notifications
 - Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`). <https://github.com/tootsuite/mastodon/pull/10373>
@@ -111,7 +172,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
 - Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
 - Mastodon API: Improve support for the user profile custom fields
-- Mastodon API: follower/following counters are nullified when `hide_follows`/`hide_followers` and `hide_follows_count`/`hide_followers_count` are set
+- Mastodon API: Add support for `fields_attributes` API parameter (setting custom fields)
+- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`)
 - Admin API: Return users' tags when querying reports
 - Admin API: Return avatar and display name when querying users
 - Admin API: Allow querying user by ID
@@ -129,11 +191,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation.
 - Pleroma API: Email change endpoint.
 - Admin API: Added moderation log
-- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
 - Web response cache (currently, enabled for ActivityPub)
-- Mastodon API: Added an endpoint to get multiple statuses by IDs (`GET /api/v1/statuses/?ids[]=1&ids[]=2`)
-- ActivityPub: Add ActivityPub actor's `discoverable` parameter.
-- Admin API: Added moderation log filters (user/start date/end date/search/pagination)
 - Reverse Proxy: Do not retry failed requests to limit pressure on the peer
 
 ### Changed
diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex
new file mode 100644 (file)
index 0000000..e378c51
--- /dev/null
@@ -0,0 +1,229 @@
+defmodule Pleroma.LoadTesting.Fetcher do
+  use Pleroma.LoadTesting.Helper
+
+  def fetch_user(user) do
+    Benchee.run(%{
+      "By id" => fn -> Repo.get_by(User, id: user.id) end,
+      "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
+      "By email" => fn -> Repo.get_by(User, email: user.email) end,
+      "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
+    })
+  end
+
+  def query_timelines(user) do
+    home_timeline_params = %{
+      "count" => 20,
+      "with_muted" => true,
+      "type" => ["Create", "Announce"],
+      "blocking_user" => user,
+      "muting_user" => user,
+      "user" => user
+    }
+
+    mastodon_public_timeline_params = %{
+      "count" => 20,
+      "local_only" => true,
+      "only_media" => "false",
+      "type" => ["Create", "Announce"],
+      "with_muted" => "true",
+      "blocking_user" => user,
+      "muting_user" => user
+    }
+
+    mastodon_federated_timeline_params = %{
+      "count" => 20,
+      "only_media" => "false",
+      "type" => ["Create", "Announce"],
+      "with_muted" => "true",
+      "blocking_user" => user,
+      "muting_user" => user
+    }
+
+    Benchee.run(%{
+      "User home timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
+          [user.ap_id | user.following],
+          home_timeline_params
+        )
+      end,
+      "User mastodon public timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+          mastodon_public_timeline_params
+        )
+      end,
+      "User mastodon federated public timeline" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+          mastodon_federated_timeline_params
+        )
+      end
+    })
+
+    home_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
+        [user.ap_id | user.following],
+        home_timeline_params
+      )
+
+    public_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params)
+
+    public_federated_activities =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
+        mastodon_federated_timeline_params
+      )
+
+    Benchee.run(%{
+      "Rendering home timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: home_activities,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering public timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: public_activities,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering public federated timeline" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: public_federated_activities,
+          for: user,
+          as: :activity
+        })
+      end
+    })
+  end
+
+  def query_notifications(user) do
+    without_muted_params = %{"count" => "20", "with_muted" => "false"}
+    with_muted_params = %{"count" => "20", "with_muted" => "true"}
+
+    Benchee.run(%{
+      "Notifications without muted" => fn ->
+        Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
+      end,
+      "Notifications with muted" => fn ->
+        Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
+      end
+    })
+
+    without_muted_notifications =
+      Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
+
+    with_muted_notifications =
+      Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
+
+    Benchee.run(%{
+      "Render notifications without muted" => fn ->
+        Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
+          notifications: without_muted_notifications,
+          for: user
+        })
+      end,
+      "Render notifications with muted" => fn ->
+        Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
+          notifications: with_muted_notifications,
+          for: user
+        })
+      end
+    })
+  end
+
+  def query_dms(user) do
+    params = %{
+      "count" => "20",
+      "with_muted" => "true",
+      "type" => "Create",
+      "blocking_user" => user,
+      "user" => user,
+      visibility: "direct"
+    }
+
+    Benchee.run(%{
+      "Direct messages with muted" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+        |> Pleroma.Pagination.fetch_paginated(params)
+      end,
+      "Direct messages without muted" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+        |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
+      end
+    })
+
+    dms_with_muted =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+      |> Pleroma.Pagination.fetch_paginated(params)
+
+    dms_without_muted =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
+      |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
+
+    Benchee.run(%{
+      "Rendering dms with muted" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: dms_with_muted,
+          for: user,
+          as: :activity
+        })
+      end,
+      "Rendering dms without muted" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+          activities: dms_without_muted,
+          for: user,
+          as: :activity
+        })
+      end
+    })
+  end
+
+  def query_long_thread(user, activity) do
+    Benchee.run(%{
+      "Fetch main post" => fn ->
+        Pleroma.Activity.get_by_id_with_object(activity.id)
+      end,
+      "Fetch context of main post" => fn ->
+        Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
+          activity.data["context"],
+          %{
+            "blocking_user" => user,
+            "user" => user,
+            "exclude_id" => activity.id
+          }
+        )
+      end
+    })
+
+    activity = Pleroma.Activity.get_by_id_with_object(activity.id)
+
+    context =
+      Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
+        activity.data["context"],
+        %{
+          "blocking_user" => user,
+          "user" => user,
+          "exclude_id" => activity.id
+        }
+      )
+
+    Benchee.run(%{
+      "Render status" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{
+          activity: activity,
+          for: user
+        })
+      end,
+      "Render context" => fn ->
+        Pleroma.Web.MastodonAPI.StatusView.render(
+          "index.json",
+          for: user,
+          activities: context,
+          as: :activity
+        )
+        |> Enum.reverse()
+      end
+    })
+  end
+end
diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex
new file mode 100644 (file)
index 0000000..5c5a5c1
--- /dev/null
@@ -0,0 +1,352 @@
+defmodule Pleroma.LoadTesting.Generator do
+  use Pleroma.LoadTesting.Helper
+  alias Pleroma.Web.CommonAPI
+
+  def generate_users(opts) do
+    IO.puts("Starting generating #{opts[:users_max]} users...")
+    {time, _} = :timer.tc(fn -> do_generate_users(opts) end)
+
+    IO.puts("Inserting users take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_users(opts) do
+    max = Keyword.get(opts, :users_max)
+
+    Task.async_stream(
+      1..max,
+      &generate_user_data(&1),
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Enum.to_list()
+  end
+
+  defp generate_user_data(i) do
+    remote = Enum.random([true, false])
+
+    user = %User{
+      name: "Test テスト User #{i}",
+      email: "user#{i}@example.com",
+      nickname: "nick#{i}",
+      password_hash:
+        "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
+      bio: "Tester Number #{i}",
+      info: %{},
+      local: remote
+    }
+
+    user_urls =
+      if remote do
+        base_url =
+          Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"])
+
+        ap_id = "#{base_url}/users/#{user.nickname}"
+
+        %{
+          ap_id: ap_id,
+          follower_address: ap_id <> "/followers",
+          following_address: ap_id <> "/following",
+          following: [ap_id]
+        }
+      else
+        %{
+          ap_id: User.ap_id(user),
+          follower_address: User.ap_followers(user),
+          following_address: User.ap_following(user),
+          following: [User.ap_id(user)]
+        }
+      end
+
+    user = Map.merge(user, user_urls)
+
+    Repo.insert!(user)
+  end
+
+  def generate_activities(user, users) do
+    do_generate_activities(user, users)
+  end
+
+  defp do_generate_activities(user, users) do
+    IO.puts("Starting generating 20000 common activities...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..20_000,
+          fn _ ->
+            do_generate_activity([user | users])
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting common activities take #{to_sec(time)} sec.\n")
+
+    IO.puts("Starting generating 20000 activities with mentions...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..20_000,
+          fn _ ->
+            do_generate_activity_with_mention(user, users)
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n")
+
+    IO.puts("Starting generating 10000 activities with threads...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..10_000,
+          fn _ ->
+            do_generate_threads([user | users])
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_activity(users) do
+    post = %{
+      "status" => "Some status without mention with random user"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  defp do_generate_activity_with_mention(user, users) do
+    mentions_cnt = Enum.random([2, 3, 4, 5])
+    with_user = Enum.random([true, false])
+    users = Enum.shuffle(users)
+    mentions_users = Enum.take(users, mentions_cnt)
+    mentions_users = if with_user, do: [user | mentions_users], else: mentions_users
+
+    mentions_str =
+      Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ")
+
+    post = %{
+      "status" => mentions_str <> "some status with mentions random users"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  defp do_generate_threads(users) do
+    thread_length = Enum.random([2, 3, 4, 5])
+    actor = Enum.random(users)
+
+    post = %{
+      "status" => "Start of the thread"
+    }
+
+    {:ok, activity} = CommonAPI.post(actor, post)
+
+    Enum.each(1..thread_length, fn _ ->
+      user = Enum.random(users)
+
+      post = %{
+        "status" => "@#{actor.nickname} reply to thread",
+        "in_reply_to_status_id" => activity.id
+      }
+
+      CommonAPI.post(user, post)
+    end)
+  end
+
+  def generate_remote_activities(user, users) do
+    do_generate_remote_activities(user, users)
+  end
+
+  defp do_generate_remote_activities(user, users) do
+    IO.puts("Starting generating 10000 remote activities...")
+
+    {time, _} =
+      :timer.tc(fn ->
+        Task.async_stream(
+          1..10_000,
+          fn i ->
+            do_generate_remote_activity(i, user, users)
+          end,
+          max_concurrency: 10,
+          timeout: 30_000
+        )
+        |> Stream.run()
+      end)
+
+    IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_remote_activity(i, user, users) do
+    actor = Enum.random(users)
+    %{host: host} = URI.parse(actor.ap_id)
+    date = Date.utc_today()
+    datetime = DateTime.utc_now()
+
+    map = %{
+      "actor" => actor.ap_id,
+      "cc" => [actor.follower_address, user.ap_id],
+      "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+      "id" => actor.ap_id <> "/statuses/#{i}/activity",
+      "object" => %{
+        "actor" => actor.ap_id,
+        "atomUri" => actor.ap_id <> "/statuses/#{i}",
+        "attachment" => [],
+        "attributedTo" => actor.ap_id,
+        "bcc" => [],
+        "bto" => [],
+        "cc" => [actor.follower_address, user.ap_id],
+        "content" =>
+          "<p><span class=\"h-card\"><a href=\"" <>
+            user.ap_id <>
+            "\" class=\"u-url mention\">@<span>" <> user.nickname <> "</span></a></span></p>",
+        "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+        "conversation" =>
+          "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
+        "emoji" => %{},
+        "id" => actor.ap_id <> "/statuses/#{i}",
+        "inReplyTo" => nil,
+        "inReplyToAtomUri" => nil,
+        "published" => datetime,
+        "sensitive" => true,
+        "summary" => "cw",
+        "tag" => [
+          %{
+            "href" => user.ap_id,
+            "name" => "@#{user.nickname}@#{host}",
+            "type" => "Mention"
+          }
+        ],
+        "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+        "type" => "Note",
+        "url" => "http://#{host}/@#{actor.nickname}/#{i}"
+      },
+      "published" => datetime,
+      "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+      "type" => "Create"
+    }
+
+    Pleroma.Web.ActivityPub.ActivityPub.insert(map, false)
+  end
+
+  def generate_dms(user, users, opts) do
+    IO.puts("Starting generating #{opts[:dms_max]} DMs")
+    {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end)
+    IO.puts("Inserting dms take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_dms(user, users, opts) do
+    Task.async_stream(
+      1..opts[:dms_max],
+      fn _ ->
+        do_generate_dm(user, users)
+      end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+  end
+
+  defp do_generate_dm(user, users) do
+    post = %{
+      "status" => "@#{user.nickname} some direct message",
+      "visibility" => "direct"
+    }
+
+    CommonAPI.post(Enum.random(users), post)
+  end
+
+  def generate_long_thread(user, users, opts) do
+    IO.puts("Starting generating long thread with #{opts[:thread_length]} replies")
+    {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end)
+    IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n")
+    {:ok, activity}
+  end
+
+  defp do_generate_long_thread(user, users, opts) do
+    {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"})
+
+    Task.async_stream(
+      1..opts[:thread_length],
+      fn _ -> do_generate_thread(users, id) end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+
+    activity
+  end
+
+  defp do_generate_thread(users, activity_id) do
+    CommonAPI.post(Enum.random(users), %{
+      "status" => "reply to main post",
+      "in_reply_to_status_id" => activity_id
+    })
+  end
+
+  def generate_non_visible_message(user, users) do
+    IO.puts("Starting generating 1000 non visible posts")
+
+    {time, _} =
+      :timer.tc(fn ->
+        do_generate_non_visible_posts(user, users)
+      end)
+
+    IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n")
+  end
+
+  defp do_generate_non_visible_posts(user, users) do
+    [not_friend | users] = users
+
+    make_friends(user, users)
+
+    Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end,
+      max_concurrency: 10,
+      timeout: 30_000
+    )
+    |> Stream.run()
+  end
+
+  defp make_friends(_user, []), do: nil
+
+  defp make_friends(user, [friend | users]) do
+    {:ok, _} = User.follow(user, friend)
+    {:ok, _} = User.follow(friend, user)
+    make_friends(user, users)
+  end
+
+  defp do_generate_non_visible_post(not_friend, users) do
+    post = %{
+      "status" => "some non visible post",
+      "visibility" => "private"
+    }
+
+    {:ok, activity} = CommonAPI.post(not_friend, post)
+
+    thread_length = Enum.random([2, 3, 4, 5])
+
+    Enum.each(1..thread_length, fn _ ->
+      user = Enum.random(users)
+
+      post = %{
+        "status" => "@#{not_friend.nickname} reply to non visible post",
+        "in_reply_to_status_id" => activity.id,
+        "visibility" => "private"
+      }
+
+      CommonAPI.post(user, post)
+    end)
+  end
+end
diff --git a/benchmarks/load_testing/helper.ex b/benchmarks/load_testing/helper.ex
new file mode 100644 (file)
index 0000000..47b25c6
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.LoadTesting.Helper do
+  defmacro __using__(_) do
+    quote do
+      import Ecto.Query
+      alias Pleroma.Repo
+      alias Pleroma.User
+
+      defp to_sec(microseconds), do: microseconds / 1_000_000
+    end
+  end
+end
diff --git a/benchmarks/mix/tasks/pleroma/load_testing.ex b/benchmarks/mix/tasks/pleroma/load_testing.ex
new file mode 100644 (file)
index 0000000..4fa3eec
--- /dev/null
@@ -0,0 +1,134 @@
+defmodule Mix.Tasks.Pleroma.LoadTesting do
+  use Mix.Task
+  use Pleroma.LoadTesting.Helper
+  import Mix.Pleroma
+  import Pleroma.LoadTesting.Generator
+  import Pleroma.LoadTesting.Fetcher
+
+  @shortdoc "Factory for generation data"
+  @moduledoc """
+  Generates data like:
+  - local/remote users
+  - local/remote activities with notifications
+  - direct messages
+  - long thread
+  - non visible posts
+
+  ## Generate data
+      MIX_ENV=benchmark mix pleroma.load_testing --users 20000 --dms 20000 --thread_length 2000
+      MIX_ENV=benchmark mix pleroma.load_testing -u 20000 -d 20000 -t 2000
+
+  Options:
+  - `--users NUMBER` - number of users to generate. Defaults to: 20000. Alias: `-u`
+  - `--dms NUMBER` - number of direct messages to generate. Defaults to: 20000. Alias `-d`
+  - `--thread_length` - number of messages in thread. Defaults to: 2000. ALias `-t`
+  """
+
+  @aliases [u: :users, d: :dms, t: :thread_length]
+  @switches [
+    users: :integer,
+    dms: :integer,
+    thread_length: :integer
+  ]
+  @users_default 20_000
+  @dms_default 1_000
+  @thread_length_default 2_000
+
+  def run(args) do
+    start_pleroma()
+    Pleroma.Config.put([:instance, :skip_thread_containment], true)
+    {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
+
+    users_max = Keyword.get(opts, :users, @users_default)
+    dms_max = Keyword.get(opts, :dms, @dms_default)
+    thread_length = Keyword.get(opts, :thread_length, @thread_length_default)
+
+    clean_tables()
+
+    opts =
+      Keyword.put(opts, :users_max, users_max)
+      |> Keyword.put(:dms_max, dms_max)
+      |> Keyword.put(:thread_length, thread_length)
+
+    generate_users(opts)
+
+    # main user for queries
+    IO.puts("Fetching local main user...")
+
+    {time, user} =
+      :timer.tc(fn ->
+        Repo.one(
+          from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1)
+        )
+      end)
+
+    IO.puts("Fetching main user take #{to_sec(time)} sec.\n")
+
+    IO.puts("Fetching local users...")
+
+    {time, users} =
+      :timer.tc(fn ->
+        Repo.all(
+          from(u in User,
+            where: u.id != ^user.id,
+            where: u.local == true,
+            order_by: fragment("RANDOM()"),
+            limit: 10
+          )
+        )
+      end)
+
+    IO.puts("Fetching local users take #{to_sec(time)} sec.\n")
+
+    IO.puts("Fetching remote users...")
+
+    {time, remote_users} =
+      :timer.tc(fn ->
+        Repo.all(
+          from(u in User,
+            where: u.id != ^user.id,
+            where: u.local == false,
+            order_by: fragment("RANDOM()"),
+            limit: 10
+          )
+        )
+      end)
+
+    IO.puts("Fetching remote users take #{to_sec(time)} sec.\n")
+
+    generate_activities(user, users)
+
+    generate_remote_activities(user, remote_users)
+
+    generate_dms(user, users, opts)
+
+    {:ok, activity} = generate_long_thread(user, users, opts)
+
+    generate_non_visible_message(user, users)
+
+    IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}")
+
+    IO.puts("Activities in DB: #{Repo.aggregate(from(a in Pleroma.Activity), :count, :id)}")
+
+    IO.puts("Objects in DB: #{Repo.aggregate(from(o in Pleroma.Object), :count, :id)}")
+
+    IO.puts(
+      "Notifications in DB: #{Repo.aggregate(from(n in Pleroma.Notification), :count, :id)}"
+    )
+
+    fetch_user(user)
+    query_timelines(user)
+    query_notifications(user)
+    query_dms(user)
+    query_long_thread(user, activity)
+    Pleroma.Config.put([:instance, :skip_thread_containment], false)
+    query_timelines(user)
+  end
+
+  defp clean_tables do
+    IO.puts("Deleting old data...\n")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;")
+    Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;")
+  end
+end
diff --git a/config/benchmark.exs b/config/benchmark.exs
new file mode 100644 (file)
index 0000000..dd99cf5
--- /dev/null
@@ -0,0 +1,84 @@
+use Mix.Config
+
+# We don't run a server during test. If one is required,
+# you can enable the server option below.
+config :pleroma, Pleroma.Web.Endpoint,
+  http: [port: 4001],
+  url: [port: 4001],
+  server: true
+
+# Disable captha for tests
+config :pleroma, Pleroma.Captcha,
+  # It should not be enabled for automatic tests
+  enabled: false,
+  # A fake captcha service for tests
+  method: Pleroma.Captcha.Mock
+
+# Print only warnings and errors during test
+config :logger, level: :warn
+
+config :pleroma, :auth, oauth_consumer_strategies: []
+
+config :pleroma, Pleroma.Upload, filters: [], link_name: false
+
+config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
+
+config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
+
+config :pleroma, :instance,
+  email: "admin@example.com",
+  notify_email: "noreply@example.com",
+  skip_thread_containment: false,
+  federating: false,
+  external_user_synchronization: false
+
+config :pleroma, :activitypub, sign_object_fetches: false
+
+# Configure your database
+config :pleroma, Pleroma.Repo,
+  adapter: Ecto.Adapters.Postgres,
+  username: "postgres",
+  password: "postgres",
+  database: "pleroma_test",
+  hostname: System.get_env("DB_HOST") || "localhost",
+  pool_size: 10
+
+# Reduce hash rounds for testing
+config :pbkdf2_elixir, rounds: 1
+
+config :tesla, adapter: Tesla.Mock
+
+config :pleroma, :rich_media,
+  enabled: false,
+  ignore_hosts: [],
+  ignore_tld: ["local", "localdomain", "lan"]
+
+config :web_push_encryption, :vapid_details,
+  subject: "mailto:administrator@example.com",
+  public_key:
+    "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
+  private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
+
+config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
+
+config :pleroma_job_queue, disabled: true
+
+config :pleroma, Pleroma.ScheduledActivity,
+  daily_user_limit: 2,
+  total_user_limit: 3,
+  enabled: false
+
+config :pleroma, :rate_limit,
+  search: [{1000, 30}, {1000, 30}],
+  app_account_creation: {10_000, 5},
+  password_reset: {1000, 30}
+
+config :pleroma, :http_security, report_uri: "https://endpoint.com"
+
+config :pleroma, :http, send_user_agent: false
+
+rum_enabled = System.get_env("RUM_ENABLED") == "true"
+config :pleroma, :database, rum_enabled: rum_enabled
+IO.puts("RUM enabled: #{rum_enabled}")
+
+config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
index f4d92102f70743f6f8d367eb977d11d0a073c7f8..d0766a6e2e63a6e00cf9938aded50e68d89076f2 100644 (file)
@@ -59,10 +59,6 @@ scheduled_jobs =
     _ -> []
   end
 
-scheduled_jobs =
-  scheduled_jobs ++
-    [{"0 */6 * * * *", {Pleroma.Web.Websub, :refresh_subscriptions, []}}]
-
 config :pleroma, Pleroma.Scheduler,
   global: true,
   overlap: true,
@@ -243,9 +239,7 @@ config :pleroma, :instance,
   federation_incoming_replies_max_depth: 100,
   federation_reachability_timeout_days: 7,
   federation_publisher_modules: [
-    Pleroma.Web.ActivityPub.Publisher,
-    Pleroma.Web.Websub,
-    Pleroma.Web.Salmon
+    Pleroma.Web.ActivityPub.Publisher
   ],
   allow_relay: true,
   rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
index b007cf69ca9a83097525d5da56012d8dd11f084d..571c64bc1189634da0f7d51055c84b1dcf25e77f 100644 (file)
@@ -581,9 +581,7 @@ config :pleroma, :config_description, [
         type: [:list, :module],
         description: "List of modules for federation publishing",
         suggestions: [
-          Pleroma.Web.ActivityPub.Publisher,
-          Pleroma.Web.Websub,
-          Pleroma.Web.Salmo
+          Pleroma.Web.ActivityPub.Publisher
         ]
       },
       %{
index 98c5ceccd54a5501dfefcd8a845d148aedca390f..36c49367384be2eb63b9089ffdfbfd6ae1f1b278 100644 (file)
@@ -1,6 +1,6 @@
 import Config
 
-config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
+config :pleroma, :instance, static: "/var/lib/pleroma/static"
 config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
 
 config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
index ee9e68cb1456c79c3231fa9a259e9dddbdfbbacb..6adeda07e7d9ec3c9c2a85433c9694a3c62efee2 100644 (file)
@@ -47,7 +47,7 @@ Authentication is required and the user must be an admin.
 }
 ```
 
-## `/api/pleroma/admin/users`
+## DEPRECATED `DELETE /api/pleroma/admin/users`
 
 ### Remove a user
 
@@ -56,6 +56,15 @@ Authentication is required and the user must be an admin.
   - `nickname`
 - Response: User’s nickname
 
+## `DELETE /api/pleroma/admin/users`
+
+### Remove a user
+
+- Method `DELETE`
+- Params:
+  - `nicknames`
+- Response: Array of user nicknames
+
 ### Create a user
 
 - Method: `POST`
@@ -154,28 +163,86 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 }
 ```
 
-### Add user in permission group
+## DEPRECATED `POST /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
+### Add user to permission group
 
-- Method: `POST`
 - Params: none
 - Response:
   - On failure: `{"error": "…"}`
   - On success: JSON of the `user.info`
 
+## `POST /api/pleroma/admin/users/permission_group/:permission_group`
+
+### Add users to permission group
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+  - On failure: `{"error": "…"}`
+  - On success: JSON of the `user.info`
+
+## DEPRECATED `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
 ### Remove user from permission group
 
-- Method: `DELETE`
 - Params: none
 - Response:
   - On failure: `{"error": "…"}`
   - On success: JSON of the `user.info`
 - Note: An admin cannot revoke their own admin status.
 
-## `/api/pleroma/admin/users/:nickname/activation_status`
+## `DELETE /api/pleroma/admin/users/permission_group/:permission_group`
+
+### Remove users from permission group
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+  - On failure: `{"error": "…"}`
+  - On success: JSON of the `user.info`
+- Note: An admin cannot revoke their own admin status.
+
+## `PATCH /api/pleroma/admin/users/activate`
+
+### Activate user
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## `PATCH /api/pleroma/admin/users/deactivate`
+
+### Deactivate user
+
+- Params:
+  - `nicknames`: nicknames array
+- Response:
+
+```json
+{
+  users: [
+    {
+      // user object
+    }
+  ]
+}
+```
+
+## DEPRECATED `PATCH /api/pleroma/admin/users/:nickname/activation_status`
 
 ### Active or deactivate a user
 
-- Method: `PUT`
 - Params:
   - `nickname`
   - `status` BOOLEAN field, false value means deactivation.
@@ -222,6 +289,14 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 - Response:
   - On success: URL of the unfollowed relay
 
+## `GET /api/pleroma/admin/relay`
+
+### List Relays
+
+- Params: none
+- Response:
+  - On success: JSON array of relays
+
 ## `/api/pleroma/admin/users/invite_token`
 
 ### Create an account registration invite token
index cfd9eeada46ced0fd7a3449e59c45fe89f8aac82..8a827ca80fb0c14a1e92bbc8945b36fb57c880a5 100644 (file)
@@ -28,7 +28,7 @@ defmodule Mix.Tasks.Pleroma.Database do
     Logger.info("Removing embedded objects")
 
     Repo.query!(
-      "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
+      "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
       [],
       timeout: :infinity
     )
@@ -126,7 +126,7 @@ defmodule Mix.Tasks.Pleroma.Database do
         set: [
           data:
             fragment(
-              "jsonb_set(?, '{likes}', '[]'::jsonb, true)",
+              "safe_jsonb_set(?, '{likes}', '[]'::jsonb, true)",
               object.data
             )
         ]
index d7a7b599fee4f3a97a52edceb043f80b282fe5e6..7ef5f9678534a8669cb434910d5b56a78b7866f3 100644 (file)
@@ -5,7 +5,6 @@
 defmodule Mix.Tasks.Pleroma.Relay do
   use Mix.Task
   import Mix.Pleroma
-  alias Pleroma.User
   alias Pleroma.Web.ActivityPub.Relay
 
   @shortdoc "Manages remote relays"
@@ -36,13 +35,10 @@ defmodule Mix.Tasks.Pleroma.Relay do
   def run(["list"]) do
     start_pleroma()
 
-    with %User{following: following} = _user <- Relay.get_actor() do
-      following
-      |> Enum.map(fn entry -> URI.parse(entry).host end)
-      |> Enum.uniq()
-      |> Enum.each(&shell_info(&1))
+    with {:ok, list} <- Relay.list() do
+      list |> Enum.each(&shell_info(&1))
     else
-      e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
+      {:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
     end
   end
 end
index 0bf218bc7d2fb6c9bf1b5c3b46bd29a59fc6e9aa..d681eecc80f95920577e6c81263800c6e3d89d53 100644 (file)
@@ -161,11 +161,6 @@ defmodule Pleroma.Application do
         id: :web_push_init,
         start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
         restart: :temporary
-      },
-      %{
-        id: :federator_init,
-        start: {Task, :start_link, [&Pleroma.Web.Federator.init/0]},
-        restart: :temporary
       }
     ]
   end
@@ -177,11 +172,6 @@ defmodule Pleroma.Application do
         start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
         restart: :temporary
       },
-      %{
-        id: :federator_init,
-        start: {Task, :start_link, [&Pleroma.Web.Federator.init/0]},
-        restart: :temporary
-      },
       %{
         id: :internal_fetch_init,
         start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
index ab81f32173fd1a69694eed8aa39c1be0013843bc..e17f49e58da622ec8db706bc2dfad9ca0a2a8f5b 100644 (file)
@@ -48,6 +48,12 @@ defmodule Pleroma.Conversation.Participation do
     |> validate_required([:read])
   end
 
+  def mark_as_read(%User{} = user, %Conversation{} = conversation) do
+    with %__MODULE__{} = participation <- for_user_and_conversation(user, conversation) do
+      mark_as_read(participation)
+    end
+  end
+
   def mark_as_read(participation) do
     participation
     |> read_cng(%{read: true})
index 352cad4335a40f9c1beea6ac5c2b258191527b72..e8884e6e81c01bf15a3e0247fb4320831647af1c 100644 (file)
@@ -86,18 +86,18 @@ defmodule Pleroma.ModerationLog do
     parsed_datetime
   end
 
-  @spec insert_log(%{actor: User, subject: User, action: String.t(), permission: String.t()}) ::
+  @spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) ::
           {:ok, ModerationLog} | {:error, any}
   def insert_log(%{
         actor: %User{} = actor,
-        subject: %User{} = subject,
+        subject: subjects,
         action: action,
         permission: permission
       }) do
     %ModerationLog{
       data: %{
         "actor" => user_to_map(actor),
-        "subject" => user_to_map(subject),
+        "subject" => user_to_map(subjects),
         "action" => action,
         "permission" => permission,
         "message" => ""
@@ -303,13 +303,16 @@ defmodule Pleroma.ModerationLog do
   end
 
   @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
-
   defp insert_log_entry_with_message(entry) do
     entry.data["message"]
     |> put_in(get_log_entry_message(entry))
     |> Repo.insert()
   end
 
+  defp user_to_map(users) when is_list(users) do
+    users |> Enum.map(&user_to_map/1)
+  end
+
   defp user_to_map(%User{} = user) do
     user
     |> Map.from_struct()
@@ -349,10 +352,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "delete",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => subjects
         }
       }) do
-    "@#{actor_nickname} deleted user @#{subject_nickname}"
+    "@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -363,12 +366,7 @@ defmodule Pleroma.ModerationLog do
           "subjects" => subjects
         }
       }) do
-    nicknames =
-      subjects
-      |> Enum.map(&"@#{&1["nickname"]}")
-      |> Enum.join(", ")
-
-    "@#{actor_nickname} created users: #{nicknames}"
+    "@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -376,10 +374,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "activate",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => users
         }
       }) do
-    "@#{actor_nickname} activated user @#{subject_nickname}"
+    "@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -387,10 +385,10 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "deactivate",
-          "subject" => %{"nickname" => subject_nickname, "type" => "user"}
+          "subject" => users
         }
       }) do
-    "@#{actor_nickname} deactivated user @#{subject_nickname}"
+    "@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -402,14 +400,9 @@ defmodule Pleroma.ModerationLog do
           "action" => "tag"
         }
       }) do
-    nicknames_string =
-      nicknames
-      |> Enum.map(&"@#{&1}")
-      |> Enum.join(", ")
-
     tags_string = tags |> Enum.join(", ")
 
-    "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}"
+    "@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -421,14 +414,9 @@ defmodule Pleroma.ModerationLog do
           "action" => "untag"
         }
       }) do
-    nicknames_string =
-      nicknames
-      |> Enum.map(&"@#{&1}")
-      |> Enum.join(", ")
-
     tags_string = tags |> Enum.join(", ")
 
-    "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}"
+    "@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -436,11 +424,11 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "grant",
-          "subject" => %{"nickname" => subject_nickname},
+          "subject" => users,
           "permission" => permission
         }
       }) do
-    "@#{actor_nickname} made @#{subject_nickname} #{permission}"
+    "@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -448,11 +436,11 @@ defmodule Pleroma.ModerationLog do
         data: %{
           "actor" => %{"nickname" => actor_nickname},
           "action" => "revoke",
-          "subject" => %{"nickname" => subject_nickname},
+          "subject" => users,
           "permission" => permission
         }
       }) do
-    "@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}"
+    "@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}"
   end
 
   @spec get_log_entry_message(ModerationLog) :: String.t()
@@ -551,4 +539,16 @@ defmodule Pleroma.ModerationLog do
       }) do
     "@#{actor_nickname} deleted status ##{subject_id}"
   end
+
+  defp nicknames_to_string(nicknames) do
+    nicknames
+    |> Enum.map(&"@#{&1}")
+    |> Enum.join(", ")
+  end
+
+  defp users_to_nicknames_string(users) do
+    users
+    |> Enum.map(&"@#{&1["nickname"]}")
+    |> Enum.join(", ")
+  end
 end
index cdfbacb0e783c3610f3b9332696c77b929fa7c05..d9b41d710cebb6296a3b2d4cb41521feb2c27030 100644 (file)
@@ -181,7 +181,7 @@ defmodule Pleroma.Object do
         data:
           fragment(
             """
-            jsonb_set(?, '{repliesCount}',
+            safe_jsonb_set(?, '{repliesCount}',
               (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true)
             """,
             o.data,
@@ -204,7 +204,7 @@ defmodule Pleroma.Object do
         data:
           fragment(
             """
-            jsonb_set(?, '{repliesCount}',
+            safe_jsonb_set(?, '{repliesCount}',
               (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true)
             """,
             o.data,
index f077a9f32436841f1ffb01975c970d3082fd80a7..68535c09e66ab962bcbcb5ce91a53303f48b6d0f 100644 (file)
@@ -32,6 +32,23 @@ defmodule Pleroma.Object.Containment do
     get_actor(%{"actor" => actor})
   end
 
+  # TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
+  # objects being present in the test suite environment.  Once these objects are
+  # removed, please also remove this.
+  if Mix.env() == :test do
+    defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
+  end
+
+  defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do
+    if id_uri.host == other_uri.host do
+      :ok
+    else
+      :error
+    end
+  end
+
+  defp compare_uris(_, _), do: :error
+
   @doc """
   Checks that an imported AP object's actor matches the domain it came from.
   """
@@ -41,11 +58,7 @@ defmodule Pleroma.Object.Containment do
     id_uri = URI.parse(id)
     actor_uri = URI.parse(get_actor(params))
 
-    if id_uri.host == actor_uri.host do
-      :ok
-    else
-      :error
-    end
+    compare_uris(actor_uri, id_uri)
   end
 
   def contain_origin(id, %{"attributedTo" => actor} = params),
@@ -57,11 +70,7 @@ defmodule Pleroma.Object.Containment do
     id_uri = URI.parse(id)
     other_uri = URI.parse(other_id)
 
-    if id_uri.host == other_uri.host do
-      :ok
-    else
-      :error
-    end
+    compare_uris(id_uri, other_uri)
   end
 
   def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}),
index 5e064fd8735a9ea9dbb48f0e13d872204db1b2b4..7758cb90b1f6637d5fdb406a5fb0fe474713c903 100644 (file)
@@ -10,7 +10,6 @@ defmodule Pleroma.Object.Fetcher do
   alias Pleroma.Signature
   alias Pleroma.Web.ActivityPub.InternalFetchActor
   alias Pleroma.Web.ActivityPub.Transmogrifier
-  alias Pleroma.Web.OStatus
 
   require Logger
   require Pleroma.Constants
@@ -67,7 +66,8 @@ defmodule Pleroma.Object.Fetcher do
          {:normalize, nil} <- {:normalize, Object.normalize(data, false)},
          params <- prepare_activity_params(data),
          {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
-         {:ok, activity} <- Transmogrifier.handle_incoming(params, options),
+         {:transmogrifier, {:ok, activity}} <-
+           {:transmogrifier, Transmogrifier.handle_incoming(params, options)},
          {:object, _data, %Object{} = object} <-
            {:object, data, Object.normalize(activity, false)} do
       {:ok, object}
@@ -75,9 +75,12 @@ defmodule Pleroma.Object.Fetcher do
       {:containment, _} ->
         {:error, "Object containment failed."}
 
-      {:error, {:reject, nil}} ->
+      {:transmogrifier, {:error, {:reject, nil}}} ->
         {:reject, nil}
 
+      {:transmogrifier, _} ->
+        {:error, "Transmogrifier failure."}
+
       {:object, data, nil} ->
         reinject_object(%Object{}, data)
 
@@ -87,15 +90,8 @@ defmodule Pleroma.Object.Fetcher do
       {:fetch_object, %Object{} = object} ->
         {:ok, object}
 
-      _e ->
-        # Only fallback when receiving a fetch/normalization error with ActivityPub
-        Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
-
-        # FIXME: OStatus Object Containment?
-        case OStatus.fetch_activity_from_url(id) do
-          {:ok, [activity | _]} -> {:ok, Object.normalize(activity, false)}
-          e -> e
-        end
+      e ->
+        e
     end
   end
 
@@ -114,7 +110,8 @@ defmodule Pleroma.Object.Fetcher do
     with {:ok, object} <- fetch_object_from_id(id, options) do
       object
     else
-      _e ->
+      e ->
+        Logger.error("Error while fetching #{id}: #{inspect(e)}")
         nil
     end
   end
@@ -161,7 +158,7 @@ defmodule Pleroma.Object.Fetcher do
 
     Logger.debug("Fetch headers: #{inspect(headers)}")
 
-    with true <- String.starts_with?(id, "http"),
+    with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
          {:ok, %{body: body, status: code}} when code in 200..299 <- HTTP.get(id, headers),
          {:ok, data} <- Jason.decode(body),
          :ok <- Containment.contain_origin_from_id(id, data) do
@@ -170,6 +167,9 @@ defmodule Pleroma.Object.Fetcher do
       {:ok, %{status: code}} when code in [404, 410] ->
         {:error, "Object has been deleted"}
 
+      {:scheme, _} ->
+        {:error, "Unsupported URI scheme"}
+
       e ->
         {:error, e}
     end
index 9f0adde5b1da66ad07b8b812774784e698f829a0..2e0986197402479bf147332bd0155e33ad0fd347 100644 (file)
@@ -105,7 +105,7 @@ defmodule Pleroma.Upload do
           {Pleroma.Config.get!([:instance, :upload_limit]), "Document"}
       end
 
-    opts = %{
+    %{
       activity_type: Keyword.get(opts, :activity_type, activity_type),
       size_limit: Keyword.get(opts, :size_limit, size_limit),
       uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])),
@@ -118,37 +118,6 @@ defmodule Pleroma.Upload do
           Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url())
         )
     }
-
-    # TODO: 1.0+ : remove old config compatibility
-    opts =
-      if Pleroma.Config.get([__MODULE__, :strip_exif]) == true &&
-           !Enum.member?(opts.filters, Pleroma.Upload.Filter.Mogrify) do
-        Logger.warn("""
-        Pleroma: configuration `:instance, :strip_exif` is deprecated, please instead set:
-
-          :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Mogrify]]
-
-          :pleroma, Pleroma.Upload.Filter.Mogrify, args: ["strip", "auto-orient"]
-        """)
-
-        Pleroma.Config.put([Pleroma.Upload.Filter.Mogrify], args: ["strip", "auto-orient"])
-        Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Mogrify])
-      else
-        opts
-      end
-
-    if Pleroma.Config.get([:instance, :dedupe_media]) == true &&
-         !Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do
-      Logger.warn("""
-      Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set:
-
-      :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]]
-      """)
-
-      Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe])
-    else
-      opts
-    end
   end
 
   defp prepare_upload(%Plug.Upload{} = file, opts) do
index 2cfb13a8c0164b2b4f1989fae7bdc6a666202c0e..ec705b8f6ca4c5702b1461f01a2fd0f7fd6639cd 100644 (file)
@@ -26,9 +26,7 @@ defmodule Pleroma.User do
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
   alias Pleroma.Web.OAuth
-  alias Pleroma.Web.OStatus
   alias Pleroma.Web.RelMe
-  alias Pleroma.Web.Websub
   alias Pleroma.Workers.BackgroundWorker
 
   require Logger
@@ -437,10 +435,6 @@ defmodule Pleroma.User do
         {:error, "Could not follow user: #{followed.nickname} blocked you."}
 
       true ->
-        if !followed.local && follower.local && !ap_enabled?(followed) do
-          Websub.subscribe(follower, followed)
-        end
-
         q =
           from(u in User,
             where: u.id == ^follower.id,
@@ -614,12 +608,7 @@ defmodule Pleroma.User do
     Cachex.fetch!(:user_cache, key, fn -> user_info(user) end)
   end
 
-  def fetch_by_nickname(nickname) do
-    case ActivityPub.make_user_from_nickname(nickname) do
-      {:ok, user} -> {:ok, user}
-      _ -> OStatus.make_user(nickname)
-    end
-  end
+  def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
 
   def get_or_fetch_by_nickname(nickname) do
     with %User{} = user <- get_by_nickname(nickname) do
@@ -725,7 +714,7 @@ defmodule Pleroma.User do
       set: [
         info:
           fragment(
-            "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)",
+            "safe_jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)",
             u.info,
             u.info
           )
@@ -746,7 +735,7 @@ defmodule Pleroma.User do
       set: [
         info:
           fragment(
-            "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)",
+            "safe_jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)",
             u.info,
             u.info
           )
@@ -816,7 +805,7 @@ defmodule Pleroma.User do
         set: [
           info:
             fragment(
-              "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
+              "safe_jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
               u.info,
               s.count
             )
@@ -1059,7 +1048,15 @@ defmodule Pleroma.User do
     BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status})
   end
 
-  def deactivate(%User{} = user, status \\ true) do
+  def deactivate(user, status \\ true)
+
+  def deactivate(users, status) when is_list(users) do
+    Repo.transaction(fn ->
+      for user <- users, do: deactivate(user, status)
+    end)
+  end
+
+  def deactivate(%User{} = user, status) do
     with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do
       Enum.each(get_followers(user), &invalidate_cache/1)
       Enum.each(get_friends(user), &update_follower_count/1)
@@ -1072,6 +1069,10 @@ defmodule Pleroma.User do
     update_info(user, &User.Info.update_notification_settings(&1, settings))
   end
 
+  def delete(users) when is_list(users) do
+    for user <- users, do: delete(user)
+  end
+
   def delete(%User{} = user) do
     BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
   end
@@ -1234,18 +1235,7 @@ defmodule Pleroma.User do
 
   def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
 
-  def fetch_by_ap_id(ap_id) do
-    case ActivityPub.make_user_from_ap_id(ap_id) do
-      {:ok, user} ->
-        {:ok, user}
-
-      _ ->
-        case OStatus.make_user(ap_id) do
-          {:ok, user} -> {:ok, user}
-          _ -> {:error, "Could not fetch by AP id"}
-        end
-    end
-  end
+  def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
 
   def get_or_fetch_by_ap_id(ap_id) do
     user = get_cached_by_ap_id(ap_id)
@@ -1300,11 +1290,6 @@ defmodule Pleroma.User do
     {:ok, key}
   end
 
-  # OStatus Magic Key
-  def public_key_from_info(%{magic_key: magic_key}) when not is_nil(magic_key) do
-    {:ok, Pleroma.Web.Salmon.decode_key(magic_key)}
-  end
-
   def public_key_from_info(_), do: {:error, "not found key"}
 
   def get_public_key_for_ap_id(ap_id) do
@@ -1625,6 +1610,12 @@ defmodule Pleroma.User do
 
   `fun` is called with the `user.info`.
   """
+  def update_info(users, fun) when is_list(users) do
+    Repo.transaction(fn ->
+      for user <- users, do: update_info(user, fun)
+    end)
+  end
+
   def update_info(user, fun) do
     user
     |> change_info(fun)
index 4b5b43d7fc346fdb77fd67da66205d40b592745c..2d39abcb34582099f983ab2ab4137980be0f8460 100644 (file)
@@ -39,9 +39,6 @@ defmodule Pleroma.User.Info do
     field(:settings, :map, default: nil)
     field(:magic_key, :string, default: nil)
     field(:uri, :string, default: nil)
-    field(:topic, :string, default: nil)
-    field(:hub, :string, default: nil)
-    field(:salmon, :string, default: nil)
     field(:hide_followers_count, :boolean, default: false)
     field(:hide_follows_count, :boolean, default: false)
     field(:hide_followers, :boolean, default: false)
@@ -262,9 +259,6 @@ defmodule Pleroma.User.Info do
       :locked,
       :magic_key,
       :uri,
-      :hub,
-      :topic,
-      :salmon,
       :hide_followers,
       :hide_follows,
       :hide_followers_count,
index 6fb2c2352f8aa9655f635ffc687ae40fb26f95ad..0d697fe3d3eeda9b3841c31f66eb1845766f6045 100644 (file)
@@ -4,11 +4,9 @@
 
 defmodule Pleroma.User.Search do
   alias Pleroma.Pagination
-  alias Pleroma.Repo
   alias Pleroma.User
   import Ecto.Query
 
-  @similarity_threshold 0.25
   @limit 20
 
   def search(query_string, opts \\ []) do
@@ -23,18 +21,10 @@ defmodule Pleroma.User.Search do
 
     maybe_resolve(resolve, for_user, query_string)
 
-    {:ok, results} =
-      Repo.transaction(fn ->
-        Ecto.Adapters.SQL.query(
-          Repo,
-          "select set_limit(#{@similarity_threshold})",
-          []
-        )
-
-        query_string
-        |> search_query(for_user, following)
-        |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
-      end)
+    results =
+      query_string
+      |> search_query(for_user, following)
+      |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
 
     results
   end
@@ -56,15 +46,65 @@ defmodule Pleroma.User.Search do
     |> base_query(following)
     |> filter_blocked_user(for_user)
     |> filter_blocked_domains(for_user)
-    |> search_subqueries(query_string)
-    |> union_subqueries
-    |> distinct_query()
-    |> boost_search_rank_query(for_user)
+    |> fts_search(query_string)
+    |> trigram_rank(query_string)
+    |> boost_search_rank(for_user)
     |> subquery()
     |> order_by(desc: :search_rank)
     |> maybe_restrict_local(for_user)
   end
 
+  @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/
+  defp fts_search(query, query_string) do
+    {nickname_weight, name_weight} =
+      if String.match?(query_string, @nickname_regex) do
+        {"A", "B"}
+      else
+        {"B", "A"}
+      end
+
+    query_string = to_tsquery(query_string)
+
+    from(
+      u in query,
+      where:
+        fragment(
+          """
+          (setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)) @@ to_tsquery('simple', ?)
+          """,
+          u.name,
+          ^name_weight,
+          u.nickname,
+          ^nickname_weight,
+          ^query_string
+        )
+    )
+  end
+
+  defp to_tsquery(query_string) do
+    String.trim_trailing(query_string, "@" <> local_domain())
+    |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
+    |> String.trim()
+    |> String.split()
+    |> Enum.map(&(&1 <> ":*"))
+    |> Enum.join(" | ")
+  end
+
+  defp trigram_rank(query, query_string) do
+    from(
+      u in query,
+      select_merge: %{
+        search_rank:
+          fragment(
+            "similarity(?, trim(? || ' ' || coalesce(?, '')))",
+            ^query_string,
+            u.nickname,
+            u.name
+          )
+      }
+    )
+  end
+
   defp base_query(_user, false), do: User
   defp base_query(user, true), do: User.get_followers_query(user)
 
@@ -87,21 +127,6 @@ defmodule Pleroma.User.Search do
 
   defp filter_blocked_domains(query, _), do: query
 
-  defp union_subqueries({fts_subquery, trigram_subquery}) do
-    from(s in trigram_subquery, union_all: ^fts_subquery)
-  end
-
-  defp search_subqueries(base_query, query_string) do
-    {
-      fts_search_subquery(base_query, query_string),
-      trigram_search_subquery(base_query, query_string)
-    }
-  end
-
-  defp distinct_query(q) do
-    from(s in subquery(q), order_by: s.search_type, distinct: s.id)
-  end
-
   defp maybe_resolve(true, user, query) do
     case {limit(), user} do
       {:all, _} -> :noop
@@ -126,9 +151,9 @@ defmodule Pleroma.User.Search do
 
   defp restrict_local(q), do: where(q, [u], u.local == true)
 
-  defp boost_search_rank_query(query, nil), do: query
+  defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
 
-  defp boost_search_rank_query(query, for_user) do
+  defp boost_search_rank(query, %User{} = for_user) do
     friends_ids = User.get_friends_ids(for_user)
     followers_ids = User.get_followers_ids(for_user)
 
@@ -137,8 +162,8 @@ defmodule Pleroma.User.Search do
         search_rank:
           fragment(
             """
-             CASE WHEN (?) THEN 0.5 + (?) * 1.3
-             WHEN (?) THEN 0.5 + (?) * 1.2
+             CASE WHEN (?) THEN (?) * 1.5
+             WHEN (?) THEN (?) * 1.3
              WHEN (?) THEN (?) * 1.1
              ELSE (?) END
             """,
@@ -154,70 +179,5 @@ defmodule Pleroma.User.Search do
     )
   end
 
-  @spec fts_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
-  defp fts_search_subquery(query, term) do
-    processed_query =
-      String.trim_trailing(term, "@" <> local_domain())
-      |> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
-      |> String.trim()
-      |> String.split()
-      |> Enum.map(&(&1 <> ":*"))
-      |> Enum.join(" | ")
-
-    from(
-      u in query,
-      select_merge: %{
-        search_type: ^0,
-        search_rank:
-          fragment(
-            """
-            ts_rank_cd(
-              setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
-              setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
-              to_tsquery('simple', ?),
-              32
-            )
-            """,
-            u.nickname,
-            u.name,
-            ^processed_query
-          )
-      },
-      where:
-        fragment(
-          """
-            (setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
-            setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
-          """,
-          u.nickname,
-          u.name,
-          ^processed_query
-        )
-    )
-    |> User.restrict_deactivated()
-  end
-
-  @spec trigram_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
-  defp trigram_search_subquery(query, term) do
-    term = String.trim_trailing(term, "@" <> local_domain())
-
-    from(
-      u in query,
-      select_merge: %{
-        # ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
-        search_type: fragment("?", 1),
-        search_rank:
-          fragment(
-            "similarity(?, trim(? || ' ' || coalesce(?, '')))",
-            ^term,
-            u.nickname,
-            u.name
-          )
-      },
-      where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
-    )
-    |> User.restrict_deactivated()
-  end
-
-  defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
+  defp boost_search_rank(query, _for_user), do: query
 end
index 1d34c4d7ef8e9f1049f4064458b70ce9246c4e10..94c467b69e72196df67da628f450b063c22b8d05 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   alias Pleroma.Activity.Ir.Topics
   alias Pleroma.Config
   alias Pleroma.Conversation
+  alias Pleroma.Conversation.Participation
   alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Object.Containment
@@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:ok, map} <- MRF.filter(map),
          {recipients, _, _} = get_recipients(map),
          {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
-         :ok <- Containment.contain_child(map),
+         {:containment, :ok} <- {:containment, Containment.contain_child(map)},
          {:ok, map, object} <- insert_full_object(map) do
       {:ok, activity} =
         Repo.insert(%Activity{
@@ -153,11 +154,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
       Notification.create_notifications(activity)
 
-      participations =
-        activity
-        |> Conversation.create_or_bump_for()
-        |> get_participations()
-
+      conversation = create_or_bump_conversation(activity, map["actor"])
+      participations = get_participations(conversation)
       stream_out(activity)
       stream_out_participations(participations)
       {:ok, activity}
@@ -182,7 +180,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  defp get_participations({:ok, %{participations: participations}}), do: participations
+  defp create_or_bump_conversation(activity, actor) do
+    with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
+         %User{} = user <- User.get_cached_by_ap_id(actor),
+         Participation.mark_as_read(user, conversation) do
+      {:ok, conversation}
+    end
+  end
+
+  defp get_participations({:ok, conversation}) do
+    conversation
+    |> Repo.preload(:participations, force: true)
+    |> Map.get(:participations)
+  end
+
   defp get_participations(_), do: []
 
   def stream_out_participations(participations) do
@@ -225,6 +236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     # only accept false as false value
     local = !(params[:local] == false)
     published = params[:published]
+    quick_insert? = Pleroma.Config.get([:env]) == :benchmark
 
     with create_data <-
            make_create_data(
@@ -235,12 +247,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:fake, false, activity} <- {:fake, fake, activity},
          _ <- increase_replies_count_if_reply(create_data),
          _ <- increase_poll_votes_if_vote(create_data),
+         {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
          # Changing note count prior to enqueuing federation task in order to avoid
          # race conditions on updating user.info
          {:ok, _actor} <- increase_note_count_if_public(actor, activity),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
     else
+      {:quick_insert, true, activity} ->
+        {:ok, activity}
+
       {:fake, true, activity} ->
         {:ok, activity}
 
@@ -1203,7 +1219,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          data <- maybe_update_follow_information(data) do
       {:ok, data}
     else
-      e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
+      e ->
+        Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
+        {:error, e}
     end
   end
 
index 3866daceee4b57a1e8ddf049aaa3b8653bb47cd5..2aac4e8b92ad90291d5682df73ed4e5642dd7415 100644 (file)
@@ -129,7 +129,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
           []
       end
 
-    Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers ++ fetchers
+    Pleroma.Web.Federator.Publisher.remote_users(actor, activity) ++ followers ++ fetchers
   end
 
   defp get_cc_ap_ids(ap_id, recipients) do
index c2ac38907dd8cd796878363248adbae74f6b7ac9..03fc434a98119975fc01dc1b603fd1e29d1de739 100644 (file)
@@ -51,6 +51,20 @@ defmodule Pleroma.Web.ActivityPub.Relay do
 
   def publish(_), do: {:error, "Not implemented"}
 
+  @spec list() :: {:ok, [String.t()]} | {:error, any()}
+  def list do
+    with %User{following: following} = _user <- get_actor() do
+      list =
+        following
+        |> Enum.map(fn entry -> URI.parse(entry).host end)
+        |> Enum.uniq()
+
+      {:ok, list}
+    else
+      error -> format_error(error)
+    end
+  end
+
   defp format_error({:error, error}), do: format_error(error)
 
   defp format_error(error) do
index b56343beb619f330a195979a246b59517e807c54..2c1ce9c550fbd8983a3f253423434d1b8636dc87 100644 (file)
@@ -1073,8 +1073,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
 
     Repo.update_all(q, [])
 
-    maybe_retire_websub(user.ap_id)
-
     q =
       from(
         a in Activity,
@@ -1117,19 +1115,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> User.update_and_set_cache()
   end
 
-  def maybe_retire_websub(ap_id) do
-    # some sanity checks
-    if is_binary(ap_id) && String.length(ap_id) > 8 do
-      q =
-        from(
-          ws in Pleroma.Web.Websub.WebsubClientSubscription,
-          where: fragment("? like ?", ws.topic, ^"#{ap_id}%")
-        )
-
-      Repo.delete_all(q)
-    end
-  end
-
   def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
     Map.put(data, "url", url["href"])
   end
index 513bae80060bc5506a1708bee93b99f357bb6ce6..b6d3f79c8a0145123c28a3c55b3e025f5ea5b548 100644 (file)
@@ -46,6 +46,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
            :user_delete,
            :users_create,
            :user_toggle_activation,
+           :user_activate,
+           :user_deactivate,
            :tag_users,
            :untag_users,
            :right_add,
@@ -98,7 +100,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
     ModerationLog.insert_log(%{
       actor: admin,
-      subject: user,
+      subject: [user],
       action: "delete"
     })
 
@@ -106,6 +108,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> json(nickname)
   end
 
+  def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+    User.delete(users)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "delete"
+    })
+
+    conn
+    |> json(nicknames)
+  end
+
   def user_follow(%{assigns: %{user: admin}} = conn, %{
         "follower" => follower_nick,
         "followed" => followed_nick
@@ -240,7 +256,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
     ModerationLog.insert_log(%{
       actor: admin,
-      subject: user,
+      subject: [user],
       action: action
     })
 
@@ -249,6 +265,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> render("show.json", %{user: updated_user})
   end
 
+  def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, false)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "activate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(updated_users)})
+  end
+
+  def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, true)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "deactivate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(updated_users)})
+  end
+
   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
     with {:ok, _} <- User.tag(nicknames, tags) do
       ModerationLog.insert_log(%{
@@ -313,6 +359,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> Enum.into(%{}, &{&1, true})
   end
 
+  def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
+        "permission_group" => permission_group,
+        "nicknames" => nicknames
+      })
+      when permission_group in ["moderator", "admin"] do
+    info = Map.put(%{}, "is_" <> permission_group, true)
+
+    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+
+    User.update_info(users, &User.Info.admin_api_update(&1, info))
+
+    ModerationLog.insert_log(%{
+      action: "grant",
+      actor: admin,
+      subject: users,
+      permission: permission_group
+    })
+
+    json(conn, info)
+  end
+
+  def right_add_multiple(conn, _) do
+    render_error(conn, :not_found, "No such permission_group")
+  end
+
   def right_add(%{assigns: %{user: admin}} = conn, %{
         "permission_group" => permission_group,
         "nickname" => nickname
@@ -328,7 +399,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     ModerationLog.insert_log(%{
       action: "grant",
       actor: admin,
-      subject: user,
+      subject: [user],
       permission: permission_group
     })
 
@@ -349,8 +420,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     })
   end
 
-  def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
-    render_error(conn, :forbidden, "You can't revoke your own admin status.")
+  def right_delete_multiple(
+        %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
+        %{
+          "permission_group" => permission_group,
+          "nicknames" => nicknames
+        }
+      )
+      when permission_group in ["moderator", "admin"] do
+    with false <- Enum.member?(nicknames, admin_nickname) do
+      info = Map.put(%{}, "is_" <> permission_group, false)
+
+      users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+
+      User.update_info(users, &User.Info.admin_api_update(&1, info))
+
+      ModerationLog.insert_log(%{
+        action: "revoke",
+        actor: admin,
+        subject: users,
+        permission: permission_group
+      })
+
+      json(conn, info)
+    else
+      _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
+    end
+  end
+
+  def right_delete_multiple(conn, _) do
+    render_error(conn, :not_found, "No such permission_group")
   end
 
   def right_delete(
@@ -371,33 +470,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     ModerationLog.insert_log(%{
       action: "revoke",
       actor: admin,
-      subject: user,
+      subject: [user],
       permission: permission_group
     })
 
     json(conn, info)
   end
 
-  def right_delete(conn, _) do
-    render_error(conn, :not_found, "No such permission_group")
+  def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
+    render_error(conn, :forbidden, "You can't revoke your own admin status.")
   end
 
-  def set_activation_status(%{assigns: %{user: admin}} = conn, %{
-        "nickname" => nickname,
-        "status" => status
-      }) do
-    with {:ok, status} <- Ecto.Type.cast(:boolean, status),
-         %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, _} <- User.deactivate(user, !status) do
-      action = if(user.info.deactivated, do: "activate", else: "deactivate")
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        subject: user,
-        action: action
-      })
-
-      json_response(conn, :no_content, "")
+  def relay_list(conn, _params) do
+    with {:ok, list} <- Relay.list() do
+      json(conn, %{relays: list})
+    else
+      _ ->
+        conn
+        |> put_status(500)
     end
   end
 
index a96affd40cbe4800b0b213c287144688f505ac62..441269162dfc700841ccf4dcf3014a5d96156172 100644 (file)
@@ -19,6 +19,12 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
     }
   end
 
+  def render("index.json", %{users: users}) do
+    %{
+      users: render_many(users, AccountView, "show.json", as: :user)
+    }
+  end
+
   def render("show.json", %{user: user}) do
     avatar = User.avatar_url(user) |> MediaProxy.url()
     display_name = HTML.strip_tags(user.name || user.nickname)
index 1a2da014ae054999325a55eaf1c844a12d1210e4..e8a56ebd706d7f243328c7cad05202bcf1b5a626 100644 (file)
@@ -10,19 +10,11 @@ defmodule Pleroma.Web.Federator do
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.Websub
   alias Pleroma.Workers.PublisherWorker
   alias Pleroma.Workers.ReceiverWorker
-  alias Pleroma.Workers.SubscriberWorker
 
   require Logger
 
-  def init do
-    # To do: consider removing this call in favor of scheduled execution (`quantum`-based)
-    refresh_subscriptions(schedule_in: 60)
-  end
-
   @doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)"
   # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
   def allowed_incoming_reply_depth?(depth) do
@@ -37,10 +29,6 @@ defmodule Pleroma.Web.Federator do
 
   # Client API
 
-  def incoming_doc(doc) do
-    ReceiverWorker.enqueue("incoming_doc", %{"body" => doc})
-  end
-
   def incoming_ap_doc(params) do
     ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params})
   end
@@ -53,18 +41,6 @@ defmodule Pleroma.Web.Federator do
     PublisherWorker.enqueue("publish", %{"activity_id" => activity.id})
   end
 
-  def verify_websub(websub) do
-    SubscriberWorker.enqueue("verify_websub", %{"websub_id" => websub.id})
-  end
-
-  def request_subscription(websub) do
-    SubscriberWorker.enqueue("request_subscription", %{"websub_id" => websub.id})
-  end
-
-  def refresh_subscriptions(worker_args \\ []) do
-    SubscriberWorker.enqueue("refresh_subscriptions", %{}, worker_args ++ [max_attempts: 1])
-  end
-
   # Job Worker Callbacks
 
   @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
@@ -81,11 +57,6 @@ defmodule Pleroma.Web.Federator do
     end
   end
 
-  def perform(:incoming_doc, doc) do
-    Logger.info("Got document, trying to parse")
-    OStatus.handle_incoming(doc)
-  end
-
   def perform(:incoming_ap_doc, params) do
     Logger.info("Handling incoming AP activity")
 
@@ -111,29 +82,6 @@ defmodule Pleroma.Web.Federator do
     end
   end
 
-  def perform(:request_subscription, websub) do
-    Logger.debug("Refreshing #{websub.topic}")
-
-    with {:ok, websub} <- Websub.request_subscription(websub) do
-      Logger.debug("Successfully refreshed #{websub.topic}")
-    else
-      _e -> Logger.debug("Couldn't refresh #{websub.topic}")
-    end
-  end
-
-  def perform(:verify_websub, websub) do
-    Logger.debug(fn ->
-      "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"
-    end)
-
-    Websub.verify(websub)
-  end
-
-  def perform(:refresh_subscriptions) do
-    Logger.debug("Federator running refresh subscriptions")
-    Websub.refresh_subscriptions()
-  end
-
   def ap_enabled_actor(id) do
     user = User.get_cached_by_ap_id(id)
 
index 937064638fc72ee9d8d1b21b80ac34656e17fc89..fb9b26649b5a3a0dff1b3c60907fb244a970ac71 100644 (file)
@@ -80,4 +80,30 @@ defmodule Pleroma.Web.Federator.Publisher do
       links ++ module.gather_nodeinfo_protocol_names()
     end)
   end
+
+  @doc """
+  Gathers a set of remote users given an IR envelope.
+  """
+  def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
+    cc = Map.get(data, "cc", [])
+
+    bcc =
+      data
+      |> Map.get("bcc", [])
+      |> Enum.reduce([], fn ap_id, bcc ->
+        case Pleroma.List.get_by_ap_id(ap_id) do
+          %Pleroma.List{user_id: ^user_id} = list ->
+            {:ok, following} = Pleroma.List.get_following(list)
+            bcc ++ Enum.map(following, & &1.ap_id)
+
+          _ ->
+            bcc
+        end
+      end)
+
+    [to, cc, bcc]
+    |> Enum.concat()
+    |> Enum.map(&User.get_cached_by_ap_id/1)
+    |> Enum.filter(fn user -> user && !user.local end)
+  end
 end
index 3c26eb4069b2a2fb7bad3986d680ee0ffe21fd01..a400d1c8d7e54186038aeb2528a32bbbec6ec609 100644 (file)
@@ -35,6 +35,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
          {_, stream} <- List.keyfind(params, "stream", 0),
          {:ok, user} <- allow_request(stream, [access_token, sec_websocket]),
          topic when is_binary(topic) <- expand_topic(stream, params) do
+      req =
+        if sec_websocket do
+          :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req)
+        else
+          req
+        end
+
       {:cowboy_websocket, req, %{user: user, topic: topic}, %{idle_timeout: @timeout}}
     else
       {:error, code} ->
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
deleted file mode 100644 (file)
index 8e55b9f..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.ActivityRepresenter do
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  require Logger
-  require Pleroma.Constants
-
-  defp get_href(id) do
-    with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
-      external_url
-    else
-      _e -> id
-    end
-  end
-
-  defp get_in_reply_to(activity) do
-    with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do
-      [
-        {:"thr:in-reply-to",
-         [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}
-      ]
-    else
-      _ ->
-        []
-    end
-  end
-
-  defp get_mentions(to) do
-    Enum.map(to, fn id ->
-      cond do
-        # Special handling for the AP/Ostatus public collections
-        Pleroma.Constants.as_public() == id ->
-          {:link,
-           [
-             rel: "mentioned",
-             "ostatus:object-type": "http://activitystrea.ms/schema/1.0/collection",
-             href: "http://activityschema.org/collection/public"
-           ], []}
-
-        # Ostatus doesn't handle follower collections, ignore these.
-        Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) ->
-          []
-
-        true ->
-          {:link,
-           [
-             rel: "mentioned",
-             "ostatus:object-type": "http://activitystrea.ms/schema/1.0/person",
-             href: id
-           ], []}
-      end
-    end)
-  end
-
-  defp get_links(%{local: true}, %{"id" => object_id}) do
-    h = fn str -> [to_charlist(str)] end
-
-    [
-      {:link, [type: ['application/atom+xml'], href: h.(object_id), rel: 'self'], []},
-      {:link, [type: ['text/html'], href: h.(object_id), rel: 'alternate'], []}
-    ]
-  end
-
-  defp get_links(%{local: false}, %{"external_url" => external_url}) do
-    h = fn str -> [to_charlist(str)] end
-
-    [
-      {:link, [type: ['text/html'], href: h.(external_url), rel: 'alternate'], []}
-    ]
-  end
-
-  defp get_links(_activity, _object_data), do: []
-
-  defp get_emoji_links(emojis) do
-    Enum.map(emojis, fn {emoji, file} ->
-      {:link, [name: to_charlist(emoji), rel: 'emoji', href: to_charlist(file)], []}
-    end)
-  end
-
-  def to_simple_form(activity, user, with_author \\ false)
-
-  def to_simple_form(%{data: %{"type" => "Create"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    object = Object.normalize(activity)
-
-    updated_at = object.data["published"]
-    inserted_at = object.data["published"]
-
-    attachments =
-      Enum.map(object.data["attachment"] || [], fn attachment ->
-        url = hd(attachment["url"])
-
-        {:link,
-         [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])],
-         []}
-      end)
-
-    in_reply_to = get_in_reply_to(activity)
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-    mentions = activity.recipients |> get_mentions
-
-    categories =
-      (object.data["tag"] || [])
-      |> Enum.map(fn tag ->
-        if is_binary(tag) do
-          {:category, [term: to_charlist(tag)], []}
-        else
-          nil
-        end
-      end)
-      |> Enum.filter(& &1)
-
-    emoji_links = get_emoji_links(object.data["emoji"] || %{})
-
-    summary =
-      if object.data["summary"] do
-        [{:summary, [], h.(object.data["summary"])}]
-      else
-        []
-      end
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']},
-      # For notes, federate the object id.
-      {:id, h.(object.data["id"])},
-      {:title, ['New note by #{user.nickname}']},
-      {:content, [type: 'html'], h.(object.data["content"] |> String.replace(~r/[\n\r]/, ""))},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}
-    ] ++
-      summary ++
-      get_links(activity, object.data) ++
-      categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links
-  end
-
-  def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-    mentions = activity.recipients |> get_mentions
-
-    [
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/favorite']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['New favorite by #{user.nickname}']},
-      {:content, [type: 'html'], ['#{user.nickname} favorited something']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
-         # For notes, federate the object id.
-         {:id, h.(activity.data["object"])}
-       ]},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []},
-      {:"thr:in-reply-to", [ref: to_charlist(activity.data["object"])], []}
-    ] ++ author ++ mentions
-  end
-
-  def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
-    retweeted_object = Object.normalize(retweeted_activity)
-    retweeted_user = User.get_cached_by_ap_id(retweeted_activity.data["actor"])
-
-    retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
-
-    mentions =
-      ([retweeted_user.ap_id] ++ activity.recipients)
-      |> Enum.uniq()
-      |> get_mentions()
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} repeated a notice']},
-      {:content, [type: 'html'], ['RT #{retweeted_object.data["content"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"ostatus:conversation", [ref: h.(activity.data["context"])],
-       h.(activity.data["context"])},
-      {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []},
-      {:"activity:object", retweeted_xml}
-    ] ++ mentions ++ author
-  end
-
-  def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    mentions = (activity.recipients || []) |> get_mentions
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} started following #{activity.data["object"]}']},
-      {:content, [type: 'html'],
-       ['#{user.nickname} started following #{activity.data["object"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
-         {:id, h.(activity.data["object"])},
-         {:uri, h.(activity.data["object"])}
-       ]},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
-    ] ++ mentions ++ author
-  end
-
-  # Only undos of follow for now. Will need to get redone once there are more
-  def to_simple_form(
-        %{data: %{"type" => "Undo", "object" => %{"type" => "Follow"} = follow_activity}} =
-          activity,
-        user,
-        with_author
-      ) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    mentions = (activity.recipients || []) |> get_mentions
-    follow_activity = Activity.normalize(follow_activity)
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
-      {:id, h.(activity.data["id"])},
-      {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
-      {:content, [type: 'html'],
-       ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)},
-      {:"activity:object",
-       [
-         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
-         {:id, h.(follow_activity.data["object"])},
-         {:uri, h.(follow_activity.data["object"])}
-       ]},
-      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
-    ] ++ mentions ++ author
-  end
-
-  def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
-    h = fn str -> [to_charlist(str)] end
-
-    updated_at = activity.data["published"]
-    inserted_at = activity.data["published"]
-
-    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
-
-    [
-      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
-      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/delete']},
-      {:id, h.(activity.data["object"])},
-      {:title, ['An object was deleted']},
-      {:content, [type: 'html'], ['An object was deleted']},
-      {:published, h.(inserted_at)},
-      {:updated, h.(updated_at)}
-    ] ++ author
-  end
-
-  def to_simple_form(_, _, _), do: nil
-
-  def wrap_with_entry(simple_form) do
-    [
-      {
-        :entry,
-        [
-          xmlns: 'http://www.w3.org/2005/Atom',
-          "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
-          "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
-          "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
-          "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
-        ],
-        simple_form
-      }
-    ]
-  end
-end
diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex
deleted file mode 100644 (file)
index b7b97e5..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FeedRepresenter do
-  alias Pleroma.User
-  alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  def to_simple_form(user, activities, _users) do
-    most_recent_update =
-      (List.first(activities) || user).updated_at
-      |> NaiveDateTime.to_iso8601()
-
-    h = fn str -> [to_charlist(str)] end
-
-    last_activity = List.last(activities)
-
-    entries =
-      activities
-      |> Enum.map(fn activity ->
-        {:entry, ActivityRepresenter.to_simple_form(activity, user)}
-      end)
-      |> Enum.filter(fn {_, form} -> form end)
-
-    [
-      {
-        :feed,
-        [
-          xmlns: 'http://www.w3.org/2005/Atom',
-          "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
-          "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
-          "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
-          "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
-        ],
-        [
-          {:id, h.(OStatus.feed_path(user))},
-          {:title, ['#{user.nickname}\'s timeline']},
-          {:updated, h.(most_recent_update)},
-          {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]},
-          {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []},
-          {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []},
-          {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'],
-           []},
-          {:author, UserRepresenter.to_simple_form(user)}
-        ] ++
-          if last_activity do
-            [
-              {:link,
-               [
-                 rel: 'next',
-                 href:
-                   to_charlist(OStatus.feed_path(user)) ++
-                     '?max_id=' ++ to_charlist(last_activity.id),
-                 type: 'application/atom+xml'
-               ], []}
-            ]
-          else
-            []
-          end ++ entries
-      }
-    ]
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/delete_handler.ex b/lib/pleroma/web/ostatus/handlers/delete_handler.ex
deleted file mode 100644 (file)
index ac2dc11..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.DeleteHandler do
-  require Logger
-  alias Pleroma.Object
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.XML
-
-  def handle_delete(entry, _doc \\ nil) do
-    with id <- XML.string_from_xpath("//id", entry),
-         %Object{} = object <- Object.normalize(id),
-         {:ok, delete} <- ActivityPub.delete(object, local: false) do
-      delete
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
deleted file mode 100644 (file)
index 2451397..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FollowHandler do
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  def handle(entry, doc) do
-    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
-         id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
-         followed_uri when not is_nil(followed_uri) <-
-           XML.string_from_xpath("/entry/activity:object/id", entry),
-         {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
-         {:locked, false} <- {:locked, followed.info.locked},
-         {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
-      User.follow(actor, followed)
-      {:ok, activity}
-    else
-      {:locked, true} ->
-        {:error, "It's not possible to follow locked accounts over OStatus"}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
deleted file mode 100644 (file)
index 7fae14f..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.NoteHandler do
-  require Logger
-  require Pleroma.Constants
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Utils
-  alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  @doc """
-  Get the context for this note. Uses this:
-  1. The context of the parent activity
-  2. The conversation reference in the ostatus xml
-  3. A newly generated context id.
-  """
-  def get_context(entry, in_reply_to) do
-    context =
-      (XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
-         XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
-      |> String.trim()
-
-    with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(in_reply_to) do
-      context
-    else
-      _e ->
-        if String.length(context) > 0 do
-          context
-        else
-          Utils.generate_context_id()
-        end
-    end
-  end
-
-  def get_people_mentions(entry) do
-    :xmerl_xpath.string(
-      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]',
-      entry
-    )
-    |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end)
-  end
-
-  def get_collection_mentions(entry) do
-    transmogrify = fn
-      "http://activityschema.org/collection/public" ->
-        Pleroma.Constants.as_public()
-
-      group ->
-        group
-    end
-
-    :xmerl_xpath.string(
-      '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]',
-      entry
-    )
-    |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end)
-  end
-
-  def get_mentions(entry) do
-    (get_people_mentions(entry) ++ get_collection_mentions(entry))
-    |> Enum.filter(& &1)
-  end
-
-  def get_emoji(entry) do
-    try do
-      :xmerl_xpath.string('//link[@rel="emoji"]', entry)
-      |> Enum.reduce(%{}, fn emoji, acc ->
-        Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji))
-      end)
-    rescue
-      _e -> nil
-    end
-  end
-
-  def make_to_list(actor, mentions) do
-    [
-      actor.follower_address
-    ] ++ mentions
-  end
-
-  def add_external_url(note, entry) do
-    url = XML.string_from_xpath("//link[@rel='alternate' and @type='text/html']/@href", entry)
-    Map.put(note, "external_url", url)
-  end
-
-  def fetch_replied_to_activity(entry, in_reply_to, options \\ []) do
-    with %Activity{} = activity <- Activity.get_create_by_object_ap_id(in_reply_to) do
-      activity
-    else
-      _e ->
-        with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
-             in_reply_to_href when not is_nil(in_reply_to_href) <-
-               XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry),
-             {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(in_reply_to_href, options) do
-          activity
-        else
-          _e -> nil
-        end
-    end
-  end
-
-  # TODO: Clean this up a bit.
-  def handle_note(entry, doc \\ nil, options \\ []) do
-    with id <- XML.string_from_xpath("//id", entry),
-         activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
-         [author] <- :xmerl_xpath.string('//author[1]', doc),
-         {:ok, actor} <- OStatus.find_make_or_update_actor(author),
-         content_html <- OStatus.get_content(entry),
-         cw <- OStatus.get_cw(entry),
-         in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
-         options <- Keyword.put(options, :depth, (options[:depth] || 0) + 1),
-         in_reply_to_activity <- fetch_replied_to_activity(entry, in_reply_to, options),
-         in_reply_to_object <-
-           (in_reply_to_activity && Object.normalize(in_reply_to_activity)) || nil,
-         in_reply_to <- (in_reply_to_object && in_reply_to_object.data["id"]) || in_reply_to,
-         attachments <- OStatus.get_attachments(entry),
-         context <- get_context(entry, in_reply_to),
-         tags <- OStatus.get_tags(entry),
-         mentions <- get_mentions(entry),
-         to <- make_to_list(actor, mentions),
-         date <- XML.string_from_xpath("//published", entry),
-         unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
-         cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []),
-         note <-
-           CommonAPI.Utils.make_note_data(
-             actor.ap_id,
-             to,
-             context,
-             content_html,
-             attachments,
-             in_reply_to_activity,
-             [],
-             cw
-           ),
-         note <- note |> Map.put("id", id) |> Map.put("tag", tags),
-         note <- note |> Map.put("published", date),
-         note <- note |> Map.put("emoji", get_emoji(entry)),
-         note <- add_external_url(note, entry),
-         note <- note |> Map.put("cc", cc),
-         # TODO: Handle this case in make_note_data
-         note <-
-           if(
-             in_reply_to && !in_reply_to_activity,
-             do: note |> Map.put("inReplyTo", in_reply_to),
-             else: note
-           ) do
-      ActivityPub.create(%{
-        to: to,
-        actor: actor,
-        context: context,
-        object: note,
-        published: date,
-        local: false,
-        additional: %{"cc" => cc}
-      })
-    else
-      %Activity{} = activity -> {:ok, activity}
-      e -> {:error, e}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
deleted file mode 100644 (file)
index 2062432..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UnfollowHandler do
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  def handle(entry, doc) do
-    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
-         id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
-         followed_uri when not is_nil(followed_uri) <-
-           XML.string_from_xpath("/entry/activity:object/id", entry),
-         {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
-         {:ok, activity} <- ActivityPub.unfollow(actor, followed, id, false) do
-      User.unfollow(actor, followed)
-      {:ok, activity}
-    end
-  end
-end
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
deleted file mode 100644 (file)
index 5de1cee..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus do
-  import Pleroma.Web.XML
-  require Logger
-
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Transmogrifier
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.OStatus.DeleteHandler
-  alias Pleroma.Web.OStatus.FollowHandler
-  alias Pleroma.Web.OStatus.NoteHandler
-  alias Pleroma.Web.OStatus.UnfollowHandler
-  alias Pleroma.Web.WebFinger
-  alias Pleroma.Web.Websub
-
-  def is_representable?(%Activity{} = activity) do
-    object = Object.normalize(activity)
-
-    cond do
-      is_nil(object) ->
-        false
-
-      Visibility.is_public?(activity) && object.data["type"] == "Note" ->
-        true
-
-      true ->
-        false
-    end
-  end
-
-  def feed_path(user), do: "#{user.ap_id}/feed.atom"
-
-  def pubsub_path(user), do: "#{Web.base_url()}/push/hub/#{user.nickname}"
-
-  def salmon_path(user), do: "#{user.ap_id}/salmon"
-
-  def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}"
-
-  def handle_incoming(xml_string, options \\ []) do
-    with doc when doc != :error <- parse_document(xml_string) do
-      with {:ok, actor_user} <- find_make_or_update_actor(doc),
-           do: Pleroma.Instances.set_reachable(actor_user.ap_id)
-
-      entries = :xmerl_xpath.string('//entry', doc)
-
-      activities =
-        Enum.map(entries, fn entry ->
-          {:xmlObj, :string, object_type} =
-            :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry)
-
-          {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry)
-          Logger.debug("Handling #{verb}")
-
-          try do
-            case verb do
-              'http://activitystrea.ms/schema/1.0/delete' ->
-                with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/follow' ->
-                with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/unfollow' ->
-                with {:ok, activity} <- UnfollowHandler.handle(entry, doc), do: activity
-
-              'http://activitystrea.ms/schema/1.0/share' ->
-                with {:ok, activity, retweeted_activity} <- handle_share(entry, doc),
-                     do: [activity, retweeted_activity]
-
-              'http://activitystrea.ms/schema/1.0/favorite' ->
-                with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc),
-                     do: [activity, favorited_activity]
-
-              _ ->
-                case object_type do
-                  'http://activitystrea.ms/schema/1.0/note' ->
-                    with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
-                         do: activity
-
-                  'http://activitystrea.ms/schema/1.0/comment' ->
-                    with {:ok, activity} <- NoteHandler.handle_note(entry, doc, options),
-                         do: activity
-
-                  _ ->
-                    Logger.error("Couldn't parse incoming document")
-                    nil
-                end
-            end
-          rescue
-            e ->
-              Logger.error("Error occured while handling activity")
-              Logger.error(xml_string)
-              Logger.error(inspect(e))
-              nil
-          end
-        end)
-        |> Enum.filter(& &1)
-
-      {:ok, activities}
-    else
-      _e -> {:error, []}
-    end
-  end
-
-  def make_share(entry, doc, retweeted_activity) do
-    with {:ok, actor} <- find_make_or_update_actor(doc),
-         %Object{} = object <- Object.normalize(retweeted_activity),
-         id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
-         {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
-      {:ok, activity}
-    end
-  end
-
-  def handle_share(entry, doc) do
-    with {:ok, retweeted_activity} <- get_or_build_object(entry),
-         {:ok, activity} <- make_share(entry, doc, retweeted_activity) do
-      {:ok, activity, retweeted_activity}
-    else
-      e -> {:error, e}
-    end
-  end
-
-  def make_favorite(entry, doc, favorited_activity) do
-    with {:ok, actor} <- find_make_or_update_actor(doc),
-         %Object{} = object <- Object.normalize(favorited_activity),
-         id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
-         {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
-      {:ok, activity}
-    end
-  end
-
-  def get_or_build_object(entry) do
-    with {:ok, activity} <- get_or_try_fetching(entry) do
-      {:ok, activity}
-    else
-      _e ->
-        with [object] <- :xmerl_xpath.string('/entry/activity:object', entry) do
-          NoteHandler.handle_note(object, object)
-        end
-    end
-  end
-
-  def get_or_try_fetching(entry) do
-    Logger.debug("Trying to get entry from db")
-
-    with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry),
-         %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do
-      {:ok, activity}
-    else
-      _ ->
-        Logger.debug("Couldn't get, will try to fetch")
-
-        with href when not is_nil(href) <-
-               string_from_xpath("//activity:object[1]/link[@type=\"text/html\"]/@href", entry),
-             {:ok, [favorited_activity]} <- fetch_activity_from_url(href) do
-          {:ok, favorited_activity}
-        else
-          e -> Logger.debug("Couldn't find href: #{inspect(e)}")
-        end
-    end
-  end
-
-  def handle_favorite(entry, doc) do
-    with {:ok, favorited_activity} <- get_or_try_fetching(entry),
-         {:ok, activity} <- make_favorite(entry, doc, favorited_activity) do
-      {:ok, activity, favorited_activity}
-    else
-      e -> {:error, e}
-    end
-  end
-
-  def get_attachments(entry) do
-    :xmerl_xpath.string('/entry/link[@rel="enclosure"]', entry)
-    |> Enum.map(fn enclosure ->
-      with href when not is_nil(href) <- string_from_xpath("/link/@href", enclosure),
-           type when not is_nil(type) <- string_from_xpath("/link/@type", enclosure) do
-        %{
-          "type" => "Attachment",
-          "url" => [
-            %{
-              "type" => "Link",
-              "mediaType" => type,
-              "href" => href
-            }
-          ]
-        }
-      end
-    end)
-    |> Enum.filter(& &1)
-  end
-
-  @doc """
-    Gets the content from a an entry.
-  """
-  def get_content(entry) do
-    string_from_xpath("//content", entry)
-  end
-
-  @doc """
-    Get the cw that mastodon uses.
-  """
-  def get_cw(entry) do
-    case string_from_xpath("/*/summary", entry) do
-      cw when not is_nil(cw) -> cw
-      _ -> nil
-    end
-  end
-
-  def get_tags(entry) do
-    :xmerl_xpath.string('//category', entry)
-    |> Enum.map(fn category -> string_from_xpath("/category/@term", category) end)
-    |> Enum.filter(& &1)
-    |> Enum.map(&String.downcase/1)
-  end
-
-  def maybe_update(doc, user) do
-    case string_from_xpath("//author[1]/ap_enabled", doc) do
-      "true" ->
-        Transmogrifier.upgrade_user_from_ap_id(user.ap_id)
-
-      _ ->
-        maybe_update_ostatus(doc, user)
-    end
-  end
-
-  def maybe_update_ostatus(doc, user) do
-    old_data = Map.take(user, [:bio, :avatar, :name])
-
-    with false <- user.local,
-         avatar <- make_avatar_object(doc),
-         bio <- string_from_xpath("//author[1]/summary", doc),
-         name <- string_from_xpath("//author[1]/poco:displayName", doc),
-         new_data <- %{
-           avatar: avatar || old_data.avatar,
-           name: name || old_data.name,
-           bio: bio || old_data.bio
-         },
-         false <- new_data == old_data do
-      change = Ecto.Changeset.change(user, new_data)
-      User.update_and_set_cache(change)
-    else
-      _ ->
-        {:ok, user}
-    end
-  end
-
-  def find_make_or_update_actor(doc) do
-    uri = string_from_xpath("//author/uri[1]", doc)
-
-    with {:ok, %User{} = user} <- find_or_make_user(uri),
-         {:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do
-      maybe_update(doc, user)
-    else
-      {:ap_enabled, true} ->
-        {:error, :invalid_protocol}
-
-      _ ->
-        {:error, :unknown_user}
-    end
-  end
-
-  @spec find_or_make_user(String.t()) :: {:ok, User.t()}
-  def find_or_make_user(uri) do
-    case User.get_by_ap_id(uri) do
-      %User{} = user -> {:ok, user}
-      _ -> make_user(uri)
-    end
-  end
-
-  @spec make_user(String.t(), boolean()) :: {:ok, User.t()} | {:error, any()}
-  def make_user(uri, update \\ false) do
-    with {:ok, info} <- gather_user_info(uri) do
-      with false <- update,
-           %User{} = user <- User.get_cached_by_ap_id(info["uri"]) do
-        {:ok, user}
-      else
-        _e -> User.insert_or_update_user(build_user_data(info))
-      end
-    end
-  end
-
-  defp build_user_data(info) do
-    %{
-      name: info["name"],
-      nickname: info["nickname"] <> "@" <> info["host"],
-      ap_id: info["uri"],
-      info: info,
-      avatar: info["avatar"],
-      bio: info["bio"]
-    }
-  end
-
-  # TODO: Just takes the first one for now.
-  def make_avatar_object(author_doc, rel \\ "avatar") do
-    href = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@href", author_doc)
-    type = string_from_xpath("//author[1]/link[@rel=\"#{rel}\"]/@type", author_doc)
-
-    if href do
-      %{
-        "type" => "Image",
-        "url" => [%{"type" => "Link", "mediaType" => type, "href" => href}]
-      }
-    else
-      nil
-    end
-  end
-
-  @spec gather_user_info(String.t()) :: {:ok, map()} | {:error, any()}
-  def gather_user_info(username) do
-    with {:ok, webfinger_data} <- WebFinger.finger(username),
-         {:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do
-      data =
-        webfinger_data
-        |> Map.merge(feed_data)
-        |> Map.put("fqn", username)
-
-      {:ok, data}
-    else
-      e ->
-        Logger.debug(fn -> "Couldn't gather info for #{username}" end)
-        {:error, e}
-    end
-  end
-
-  # Regex-based 'parsing' so we don't have to pull in a full html parser
-  # It's a hack anyway. Maybe revisit this in the future
-  @mastodon_regex ~r/<link href='(.*)' rel='alternate' type='application\/atom\+xml'>/
-  @gs_regex ~r/<link title=.* href="(.*)" type="application\/atom\+xml" rel="alternate">/
-  @gs_classic_regex ~r/<link rel="alternate" href="(.*)" type="application\/atom\+xml" title=.*>/
-  def get_atom_url(body) do
-    cond do
-      Regex.match?(@mastodon_regex, body) ->
-        [[_, match]] = Regex.scan(@mastodon_regex, body)
-        {:ok, match}
-
-      Regex.match?(@gs_regex, body) ->
-        [[_, match]] = Regex.scan(@gs_regex, body)
-        {:ok, match}
-
-      Regex.match?(@gs_classic_regex, body) ->
-        [[_, match]] = Regex.scan(@gs_classic_regex, body)
-        {:ok, match}
-
-      true ->
-        Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end)
-        {:error, "Couldn't find the Atom link"}
-    end
-  end
-
-  def fetch_activity_from_atom_url(url, options \\ []) do
-    with true <- String.starts_with?(url, "http"),
-         {:ok, %{body: body, status: code}} when code in 200..299 <-
-           HTTP.get(url, [{:Accept, "application/atom+xml"}]) do
-      Logger.debug("Got document from #{url}, handling...")
-      handle_incoming(body, options)
-    else
-      e ->
-        Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-        e
-    end
-  end
-
-  def fetch_activity_from_html_url(url, options \\ []) do
-    Logger.debug("Trying to fetch #{url}")
-
-    with true <- String.starts_with?(url, "http"),
-         {:ok, %{body: body}} <- HTTP.get(url, []),
-         {:ok, atom_url} <- get_atom_url(body) do
-      fetch_activity_from_atom_url(atom_url, options)
-    else
-      e ->
-        Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-        e
-    end
-  end
-
-  def fetch_activity_from_url(url, options \\ []) do
-    with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url, options) do
-      {:ok, activities}
-    else
-      _e -> fetch_activity_from_html_url(url, options)
-    end
-  rescue
-    e ->
-      Logger.debug("Couldn't get #{url}: #{inspect(e)}")
-      {:error, "Couldn't get #{url}: #{inspect(e)}"}
-  end
-end
index 20f2d9ddc58ef507ccec9384569cb9ec7cc56008..6958519de05d95acfadeecd749af7a52ec82d778 100644 (file)
@@ -13,19 +13,14 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Endpoint
-  alias Pleroma.Web.Federator
   alias Pleroma.Web.Metadata.PlayerView
-  alias Pleroma.Web.OStatus.ActivityRepresenter
   alias Pleroma.Web.Router
-  alias Pleroma.Web.XML
 
   plug(
     Pleroma.Plugs.RateLimiter,
     {:ap_routes, params: ["uuid"]} when action in [:object, :activity]
   )
 
-  plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
-
   plug(
     Pleroma.Plugs.SetFormatPlug
     when action in [:object, :activity, :notice]
@@ -33,32 +28,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
 
   action_fallback(:errors)
 
-  defp decode_or_retry(body) do
-    with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
-         {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
-      {:ok, doc}
-    else
-      _e ->
-        with [decoded | _] <- Pleroma.Web.Salmon.decode(body),
-             doc <- XML.parse_document(decoded),
-             uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
-             {:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true),
-             {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
-             {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
-          {:ok, doc}
-        end
-    end
-  end
-
-  def salmon_incoming(conn, _) do
-    {:ok, body, _conn} = read_body(conn)
-    {:ok, doc} = decode_or_retry(body)
-
-    Federator.incoming_doc(doc)
-
-    send_resp(conn, 200, "")
-  end
-
   def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
       when format in ["json", "activity+json"] do
     ActivityPubController.call(conn, :object)
@@ -179,23 +148,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do
     |> render("object.json", %{object: object})
   end
 
-  defp represent_activity(_conn, "activity+json", _, _) do
+  defp represent_activity(_conn, _, _, _) do
     {:error, :not_found}
   end
 
-  defp represent_activity(conn, _, activity, user) do
-    response =
-      activity
-      |> ActivityRepresenter.to_simple_form(user, true)
-      |> ActivityRepresenter.wrap_with_entry()
-      |> :xmerl.export_simple(:xmerl_xml)
-      |> to_string
-
-    conn
-    |> put_resp_content_type("application/atom+xml")
-    |> send_resp(200, response)
-  end
-
   def errors(conn, {:error, :not_found}) do
     render_error(conn, :not_found, "Not found")
   end
diff --git a/lib/pleroma/web/ostatus/user_representer.ex b/lib/pleroma/web/ostatus/user_representer.ex
deleted file mode 100644 (file)
index 852be6e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UserRepresenter do
-  alias Pleroma.User
-
-  def to_simple_form(user) do
-    ap_id = to_charlist(user.ap_id)
-    nickname = to_charlist(user.nickname)
-    name = to_charlist(user.name)
-    bio = to_charlist(user.bio)
-    avatar_url = to_charlist(User.avatar_url(user))
-
-    banner =
-      if banner_url = User.banner_url(user) do
-        [{:link, [rel: 'header', href: banner_url], []}]
-      else
-        []
-      end
-
-    ap_enabled =
-      if user.local do
-        [{:ap_enabled, ['true']}]
-      else
-        []
-      end
-
-    [
-      {:id, [ap_id]},
-      {:"activity:object", ['http://activitystrea.ms/schema/1.0/person']},
-      {:uri, [ap_id]},
-      {:"poco:preferredUsername", [nickname]},
-      {:"poco:displayName", [name]},
-      {:"poco:note", [bio]},
-      {:summary, [bio]},
-      {:name, [nickname]},
-      {:link, [rel: 'avatar', href: avatar_url], []}
-    ] ++ banner ++ ap_enabled
-  end
-end
index ae799b8ac36650112f23e7756d0cf475754557a0..d68fb87da21276e1d5ae4d7e6881cf015b3c63f2 100644 (file)
@@ -137,11 +137,14 @@ defmodule Pleroma.Web.Router do
     delete("/users", AdminAPIController, :user_delete)
     post("/users", AdminAPIController, :users_create)
     patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
+    patch("/users/activate", AdminAPIController, :user_activate)
+    patch("/users/deactivate", AdminAPIController, :user_deactivate)
     put("/users/tag", AdminAPIController, :tag_users)
     delete("/users/tag", AdminAPIController, :untag_users)
 
     get("/users/:nickname/permission_group", AdminAPIController, :right_get)
     get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
+
     post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
 
     delete(
@@ -150,8 +153,15 @@ defmodule Pleroma.Web.Router do
       :right_delete
     )
 
-    put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status)
+    post("/users/permission_group/:permission_group", AdminAPIController, :right_add_multiple)
+
+    delete(
+      "/users/permission_group/:permission_group",
+      AdminAPIController,
+      :right_delete_multiple
+    )
 
+    get("/relay", AdminAPIController, :relay_list)
     post("/relay", AdminAPIController, :relay_follow)
     delete("/relay", AdminAPIController, :relay_unfollow)
 
@@ -499,11 +509,6 @@ defmodule Pleroma.Web.Router do
     get("/users/:nickname/feed", Feed.FeedController, :feed)
     get("/users/:nickname", Feed.FeedController, :feed_redirect)
 
-    post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming)
-    post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
-    get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
-    post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
-
     get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
   end
 
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
deleted file mode 100644 (file)
index 0ffe903..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Salmon do
-  @behaviour Pleroma.Web.Federator.Publisher
-
-  use Bitwise
-
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Instances
-  alias Pleroma.Keys
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.XML
-
-  require Logger
-
-  def decode(salmon) do
-    doc = XML.parse_document(salmon)
-
-    {:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc)
-    {:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc)
-    {:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc)
-    {:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc)
-    {:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc)
-
-    {:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace)
-    {:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace)
-    alg = to_string(alg)
-    encoding = to_string(encoding)
-    type = to_string(type)
-
-    [data, type, encoding, alg, sig]
-  end
-
-  def fetch_magic_key(salmon) do
-    with [data, _, _, _, _] <- decode(salmon),
-         doc <- XML.parse_document(data),
-         uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
-         {:ok, public_key} <- User.get_public_key_for_ap_id(uri),
-         magic_key <- encode_key(public_key) do
-      {:ok, magic_key}
-    end
-  end
-
-  def decode_and_validate(magickey, salmon) do
-    [data, type, encoding, alg, sig] = decode(salmon)
-
-    signed_text =
-      [data, type, encoding, alg]
-      |> Enum.map(&Base.url_encode64/1)
-      |> Enum.join(".")
-
-    key = decode_key(magickey)
-
-    verify = :public_key.verify(signed_text, :sha256, sig, key)
-
-    if verify do
-      {:ok, data}
-    else
-      :error
-    end
-  end
-
-  def decode_key("RSA." <> magickey) do
-    make_integer = fn bin ->
-      list = :erlang.binary_to_list(bin)
-      Enum.reduce(list, 0, fn el, acc -> acc <<< 8 ||| el end)
-    end
-
-    [modulus, exponent] =
-      magickey
-      |> String.split(".")
-      |> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end)
-      |> Enum.map(make_integer)
-
-    {:RSAPublicKey, modulus, exponent}
-  end
-
-  def encode_key({:RSAPublicKey, modulus, exponent}) do
-    modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64()
-    exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64()
-
-    "RSA.#{modulus_enc}.#{exponent_enc}"
-  end
-
-  def encode(private_key, doc) do
-    type = "application/atom+xml"
-    encoding = "base64url"
-    alg = "RSA-SHA256"
-
-    signed_text =
-      [doc, type, encoding, alg]
-      |> Enum.map(&Base.url_encode64/1)
-      |> Enum.join(".")
-
-    signature =
-      signed_text
-      |> :public_key.sign(:sha256, private_key)
-      |> to_string
-      |> Base.url_encode64()
-
-    doc_base64 =
-      doc
-      |> Base.url_encode64()
-
-    # Don't need proper xml building, these strings are safe to leave unescaped
-    salmon = """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <me:env xmlns:me="http://salmon-protocol.org/ns/magic-env">
-      <me:data type="application/atom+xml">#{doc_base64}</me:data>
-      <me:encoding>#{encoding}</me:encoding>
-      <me:alg>#{alg}</me:alg>
-      <me:sig>#{signature}</me:sig>
-    </me:env>
-    """
-
-    {:ok, salmon}
-  end
-
-  def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
-    cc = Map.get(data, "cc", [])
-
-    bcc =
-      data
-      |> Map.get("bcc", [])
-      |> Enum.reduce([], fn ap_id, bcc ->
-        case Pleroma.List.get_by_ap_id(ap_id) do
-          %Pleroma.List{user_id: ^user_id} = list ->
-            {:ok, following} = Pleroma.List.get_following(list)
-            bcc ++ Enum.map(following, & &1.ap_id)
-
-          _ ->
-            bcc
-        end
-      end)
-
-    [to, cc, bcc]
-    |> Enum.concat()
-    |> Enum.map(&User.get_cached_by_ap_id/1)
-    |> Enum.filter(fn user -> user && !user.local end)
-  end
-
-  @doc "Pushes an activity to remote account."
-  def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
-    do: publish_one(Map.put(params, :recipient, salmon))
-
-  def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
-    with {:ok, %{status: code}} when code in 200..299 <-
-           HTTP.post(
-             url,
-             feed,
-             [{"Content-Type", "application/magic-envelope+xml"}]
-           ) do
-      if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
-        do: Instances.set_reachable(url)
-
-      Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
-      {:ok, code}
-    else
-      e ->
-        unless params[:unreachable_since], do: Instances.set_reachable(url)
-        Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
-        {:error, "Unreachable instance"}
-    end
-  end
-
-  def publish_one(%{recipient_id: recipient_id} = params) do
-    recipient = User.get_cached_by_id(recipient_id)
-
-    params
-    |> Map.delete(:recipient_id)
-    |> Map.put(:recipient, recipient)
-    |> publish_one()
-  end
-
-  def publish_one(_), do: :noop
-
-  @supported_activities [
-    "Create",
-    "Follow",
-    "Like",
-    "Announce",
-    "Undo",
-    "Delete"
-  ]
-
-  def is_representable?(%Activity{data: %{"type" => type}} = activity)
-      when type in @supported_activities,
-      do: Visibility.is_public?(activity)
-
-  def is_representable?(_), do: false
-
-  @doc """
-  Publishes an activity to remote accounts
-  """
-  @spec publish(User.t(), Pleroma.Activity.t()) :: none
-  def publish(user, activity)
-
-  def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity)
-      when type in @supported_activities do
-    feed = ActivityRepresenter.to_simple_form(activity, user, true)
-
-    if feed do
-      feed =
-        ActivityRepresenter.wrap_with_entry(feed)
-        |> :xmerl.export_simple(:xmerl_xml)
-        |> to_string
-
-      {:ok, private, _} = Keys.keys_from_pem(keys)
-      {:ok, feed} = encode(private, feed)
-
-      remote_users = remote_users(user, activity)
-
-      salmon_urls = Enum.map(remote_users, & &1.info.salmon)
-      reachable_urls_metadata = Instances.filter_reachable(salmon_urls)
-      reachable_urls = Map.keys(reachable_urls_metadata)
-
-      remote_users
-      |> Enum.filter(&(&1.info.salmon in reachable_urls))
-      |> Enum.each(fn remote_user ->
-        Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end)
-
-        Publisher.enqueue_one(__MODULE__, %{
-          recipient_id: remote_user.id,
-          feed: feed,
-          unreachable_since: reachable_urls_metadata[remote_user.info.salmon]
-        })
-      end)
-    end
-  end
-
-  def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
-
-  def gather_webfinger_links(%User{} = user) do
-    {:ok, _private, public} = Keys.keys_from_pem(user.keys)
-    magic_key = encode_key(public)
-
-    [
-      %{"rel" => "salmon", "href" => OStatus.salmon_path(user)},
-      %{
-        "rel" => "magic-public-key",
-        "href" => "data:application/magic-public-key,#{magic_key}"
-      }
-    ]
-  end
-
-  def gather_nodeinfo_protocol_names, do: []
-end
index 8cf719277f4774de6197cb20b283c51bc2a3ebfb..2fc7ac8cf694a2b4a6ecd30a3466b0c967fa7ee5 100644 (file)
@@ -49,7 +49,7 @@ defmodule Pleroma.Web.Streamer do
     end
   end
 
-  defp handle_should_send(_) do
-    true
-  end
+  defp handle_should_send(:benchmark), do: false
+
+  defp handle_should_send(_), do: true
 end
index fbfdc46b542505e7fce2e0331b50b03b8184c802..45df9dc09a20594a721d7020e835df2cb0098ba4 100644 (file)
@@ -10,8 +10,6 @@
   <title><%= @user.nickname <> "'s timeline" %></title>
   <updated><%= most_recent_update(@activities, @user) %></updated>
   <logo><%= logo(@user) %></logo>
-  <link rel="hub" href="<%= websub_url(@conn, :websub_subscription_request, @user.nickname) %>"/>
-  <link rel="salmon" href="<%= o_status_url(@conn, :salmon_incoming, @user.nickname) %>"/>
   <link rel="self" href="<%= '#{feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
 
   <%= render @view_module, "_author.xml", assigns %>
index ecb39ee503b7d73c576b83118995316b228e49f2..b4cc801799e97a5fa3224875136bd0a645e98acb 100644 (file)
@@ -108,7 +108,6 @@ defmodule Pleroma.Web.WebFinger do
              doc
            ),
          subject <- XML.string_from_xpath("//Subject", doc),
-         salmon <- XML.string_from_xpath(~s{//Link[@rel="salmon"]/@href}, doc),
          subscribe_address <-
            XML.string_from_xpath(
              ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template},
@@ -123,7 +122,6 @@ defmodule Pleroma.Web.WebFinger do
         "magic_key" => magic_key,
         "topic" => topic,
         "subject" => subject,
-        "salmon" => salmon,
         "subscribe_address" => subscribe_address,
         "ap_id" => ap_id
       }
@@ -148,16 +146,6 @@ defmodule Pleroma.Web.WebFinger do
           {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
             Map.put(data, "ap_id", link["href"])
 
-          {_, "magic-public-key"} ->
-            "data:application/magic-public-key," <> magic_key = link["href"]
-            Map.put(data, "magic_key", magic_key)
-
-          {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} ->
-            Map.put(data, "topic", link["href"])
-
-          {_, "salmon"} ->
-            Map.put(data, "salmon", link["href"])
-
           {_, "http://ostatus.org/schema/1.0/subscribe"} ->
             Map.put(data, "subscribe_address", link["template"])
 
diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex
deleted file mode 100644 (file)
index b61f388..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub do
-  alias Ecto.Changeset
-  alias Pleroma.Activity
-  alias Pleroma.HTTP
-  alias Pleroma.Instances
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.Visibility
-  alias Pleroma.Web.Endpoint
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.FeedRepresenter
-  alias Pleroma.Web.Router.Helpers
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-  alias Pleroma.Web.Websub.WebsubServerSubscription
-  alias Pleroma.Web.XML
-  require Logger
-
-  import Ecto.Query
-
-  @behaviour Pleroma.Web.Federator.Publisher
-
-  def verify(subscription, getter \\ &HTTP.get/3) do
-    challenge = Base.encode16(:crypto.strong_rand_bytes(8))
-    lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
-    lease_seconds = lease_seconds |> to_string
-
-    params = %{
-      "hub.challenge": challenge,
-      "hub.lease_seconds": lease_seconds,
-      "hub.topic": subscription.topic,
-      "hub.mode": "subscribe"
-    }
-
-    url = hd(String.split(subscription.callback, "?"))
-    query = URI.parse(subscription.callback).query || ""
-    params = Map.merge(params, URI.decode_query(query))
-
-    with {:ok, response} <- getter.(url, [], params: params),
-         ^challenge <- response.body do
-      changeset = Changeset.change(subscription, %{state: "active"})
-      Repo.update(changeset)
-    else
-      e ->
-        Logger.debug("Couldn't verify subscription")
-        Logger.debug(inspect(e))
-        {:error, subscription}
-    end
-  end
-
-  @supported_activities [
-    "Create",
-    "Follow",
-    "Like",
-    "Announce",
-    "Undo",
-    "Delete"
-  ]
-
-  def is_representable?(%Activity{data: %{"type" => type}} = activity)
-      when type in @supported_activities,
-      do: Visibility.is_public?(activity)
-
-  def is_representable?(_), do: false
-
-  def publish(topic, user, %{data: %{"type" => type}} = activity)
-      when type in @supported_activities do
-    response =
-      user
-      |> FeedRepresenter.to_simple_form([activity], [user])
-      |> :xmerl.export_simple(:xmerl_xml)
-      |> to_string
-
-    query =
-      from(
-        sub in WebsubServerSubscription,
-        where: sub.topic == ^topic and sub.state == "active",
-        where: fragment("? > (NOW() at time zone 'UTC')", sub.valid_until)
-      )
-
-    subscriptions = Repo.all(query)
-
-    callbacks = Enum.map(subscriptions, & &1.callback)
-    reachable_callbacks_metadata = Instances.filter_reachable(callbacks)
-    reachable_callbacks = Map.keys(reachable_callbacks_metadata)
-
-    subscriptions
-    |> Enum.filter(&(&1.callback in reachable_callbacks))
-    |> Enum.each(fn sub ->
-      data = %{
-        xml: response,
-        topic: topic,
-        callback: sub.callback,
-        secret: sub.secret,
-        unreachable_since: reachable_callbacks_metadata[sub.callback]
-      }
-
-      Publisher.enqueue_one(__MODULE__, data)
-    end)
-  end
-
-  def publish(_, _, _), do: ""
-
-  def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
-
-  def sign(secret, doc) do
-    :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase()
-  end
-
-  def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do
-    with {:ok, topic} <- valid_topic(params, user),
-         {:ok, lease_time} <- lease_time(params),
-         secret <- params["hub.secret"],
-         callback <- params["hub.callback"] do
-      subscription = get_subscription(topic, callback)
-
-      data = %{
-        state: subscription.state || "requested",
-        topic: topic,
-        secret: secret,
-        callback: callback
-      }
-
-      change = Changeset.change(subscription, data)
-      websub = Repo.insert_or_update!(change)
-
-      change =
-        Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)})
-
-      websub = Repo.update!(change)
-
-      Federator.verify_websub(websub)
-
-      {:ok, websub}
-    else
-      {:error, reason} ->
-        Logger.debug("Couldn't create subscription")
-        Logger.debug(inspect(reason))
-
-        {:error, reason}
-    end
-  end
-
-  def incoming_subscription_request(user, params) do
-    Logger.info("Unhandled WebSub request for #{user.nickname}: #{inspect(params)}")
-
-    {:error, "Invalid WebSub request"}
-  end
-
-  defp get_subscription(topic, callback) do
-    Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) ||
-      %WebsubServerSubscription{}
-  end
-
-  # Temp hack for mastodon.
-  defp lease_time(%{"hub.lease_seconds" => ""}) do
-    # three days
-    {:ok, 60 * 60 * 24 * 3}
-  end
-
-  defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do
-    {:ok, String.to_integer(lease_seconds)}
-  end
-
-  defp lease_time(_) do
-    # three days
-    {:ok, 60 * 60 * 24 * 3}
-  end
-
-  defp valid_topic(%{"hub.topic" => topic}, user) do
-    if topic == OStatus.feed_path(user) do
-      {:ok, OStatus.feed_path(user)}
-    else
-      {:error, "Wrong topic requested, expected #{OStatus.feed_path(user)}, got #{topic}"}
-    end
-  end
-
-  def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
-    topic = subscribed.info.topic
-    # FIXME: Race condition, use transactions
-    {:ok, subscription} =
-      with subscription when not is_nil(subscription) <-
-             Repo.get_by(WebsubClientSubscription, topic: topic) do
-        subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq()
-        change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
-        Repo.update(change)
-      else
-        _e ->
-          subscription = %WebsubClientSubscription{
-            topic: topic,
-            hub: subscribed.info.hub,
-            subscribers: [subscriber.ap_id],
-            state: "requested",
-            secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(),
-            user: subscribed
-          }
-
-          Repo.insert(subscription)
-      end
-
-    requester.(subscription)
-  end
-
-  def gather_feed_data(topic, getter \\ &HTTP.get/1) do
-    with {:ok, response} <- getter.(topic),
-         status when status in 200..299 <- response.status,
-         body <- response.body,
-         doc <- XML.parse_document(body),
-         uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc),
-         hub when not is_nil(hub) <- XML.string_from_xpath(~S{/feed/link[@rel="hub"]/@href}, doc) do
-      name = XML.string_from_xpath("/feed/author[1]/name", doc)
-      preferred_username = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc)
-      display_name = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc)
-      avatar = OStatus.make_avatar_object(doc)
-      bio = XML.string_from_xpath("/feed/author[1]/summary", doc)
-
-      {:ok,
-       %{
-         "uri" => uri,
-         "hub" => hub,
-         "nickname" => preferred_username || name,
-         "name" => display_name || name,
-         "host" => URI.parse(uri).host,
-         "avatar" => avatar,
-         "bio" => bio
-       }}
-    else
-      e ->
-        {:error, e}
-    end
-  end
-
-  def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
-    data = [
-      "hub.mode": "subscribe",
-      "hub.topic": websub.topic,
-      "hub.secret": websub.secret,
-      "hub.callback": Helpers.websub_url(Endpoint, :websub_subscription_confirmation, websub.id)
-    ]
-
-    # This checks once a second if we are confirmed yet
-    websub_checker = fn ->
-      helper = fn helper ->
-        :timer.sleep(1000)
-        websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted")
-        if websub, do: websub, else: helper.(helper)
-      end
-
-      helper.(helper)
-    end
-
-    task = Task.async(websub_checker)
-
-    with {:ok, %{status: 202}} <-
-           poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"),
-         {:ok, websub} <- Task.yield(task, timeout) do
-      {:ok, websub}
-    else
-      e ->
-        Task.shutdown(task)
-
-        change = Ecto.Changeset.change(websub, %{state: "rejected"})
-        {:ok, websub} = Repo.update(change)
-
-        Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end)
-        Logger.debug(fn -> "error: #{inspect(e)}" end)
-
-        {:error, websub}
-    end
-  end
-
-  def refresh_subscriptions(delta \\ 60 * 60 * 24) do
-    Logger.debug("Refreshing subscriptions")
-
-    cut_off = NaiveDateTime.add(NaiveDateTime.utc_now(), delta)
-
-    query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off)
-
-    subs = Repo.all(query)
-
-    Enum.each(subs, fn sub ->
-      Federator.request_subscription(sub)
-    end)
-  end
-
-  def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} = params) do
-    signature = sign(secret || "", xml)
-    Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
-
-    with {:ok, %{status: code}} when code in 200..299 <-
-           HTTP.post(
-             callback,
-             xml,
-             [
-               {"Content-Type", "application/atom+xml"},
-               {"X-Hub-Signature", "sha1=#{signature}"}
-             ]
-           ) do
-      if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
-        do: Instances.set_reachable(callback)
-
-      Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
-      {:ok, code}
-    else
-      {_post_result, response} ->
-        unless params[:unreachable_since], do: Instances.set_reachable(callback)
-        Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(response)}" end)
-        {:error, response}
-    end
-  end
-
-  def gather_webfinger_links(%User{} = user) do
-    [
-      %{
-        "rel" => "http://schemas.google.com/g/2010#updates-from",
-        "type" => "application/atom+xml",
-        "href" => OStatus.feed_path(user)
-      },
-      %{
-        "rel" => "http://ostatus.org/schema/1.0/subscribe",
-        "template" => OStatus.remote_follow_path()
-      }
-    ]
-  end
-
-  def gather_nodeinfo_protocol_names, do: ["ostatus"]
-end
diff --git a/lib/pleroma/web/websub/websub_client_subscription.ex b/lib/pleroma/web/websub/websub_client_subscription.ex
deleted file mode 100644 (file)
index 23a04b8..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubClientSubscription do
-  use Ecto.Schema
-  alias Pleroma.User
-
-  schema "websub_client_subscriptions" do
-    field(:topic, :string)
-    field(:secret, :string)
-    field(:valid_until, :naive_datetime_usec)
-    field(:state, :string)
-    field(:subscribers, {:array, :string}, default: [])
-    field(:hub, :string)
-    belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
-
-    timestamps()
-  end
-end
diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex
deleted file mode 100644 (file)
index 9e8b48b..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubController do
-  use Pleroma.Web, :controller
-
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-
-  require Logger
-
-  plug(
-    Pleroma.Web.FederatingPlug
-    when action in [
-           :websub_subscription_request,
-           :websub_subscription_confirmation,
-           :websub_incoming
-         ]
-  )
-
-  def websub_subscription_request(conn, %{"nickname" => nickname} = params) do
-    user = User.get_cached_by_nickname(nickname)
-
-    with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do
-      conn
-      |> send_resp(202, "Accepted")
-    else
-      {:error, reason} ->
-        conn
-        |> send_resp(500, reason)
-    end
-  end
-
-  # TODO: Extract this into the Websub module
-  def websub_subscription_confirmation(
-        conn,
-        %{
-          "id" => id,
-          "hub.mode" => "subscribe",
-          "hub.challenge" => challenge,
-          "hub.topic" => topic
-        } = params
-      ) do
-    Logger.debug("Got WebSub confirmation")
-    Logger.debug(inspect(params))
-
-    lease_seconds =
-      if params["hub.lease_seconds"] do
-        String.to_integer(params["hub.lease_seconds"])
-      else
-        # Guess 3 days
-        60 * 60 * 24 * 3
-      end
-
-    with %WebsubClientSubscription{} = websub <-
-           Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do
-      valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), lease_seconds)
-      change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until})
-      {:ok, _websub} = Repo.update(change)
-
-      conn
-      |> send_resp(200, challenge)
-    else
-      _e ->
-        conn
-        |> send_resp(500, "Error")
-    end
-  end
-
-  def websub_subscription_confirmation(conn, params) do
-    Logger.info("Invalid WebSub confirmation request: #{inspect(params)}")
-
-    conn
-    |> send_resp(500, "Invalid parameters")
-  end
-
-  def websub_incoming(conn, %{"id" => id}) do
-    with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")),
-         signature <- String.downcase(signature),
-         %WebsubClientSubscription{} = websub <- Repo.get(WebsubClientSubscription, id),
-         {:ok, body, _conn} = read_body(conn),
-         ^signature <- Websub.sign(websub.secret, body) do
-      Federator.incoming_doc(body)
-
-      conn
-      |> send_resp(200, "OK")
-    else
-      _e ->
-        Logger.debug("Can't handle incoming subscription post")
-
-        conn
-        |> send_resp(500, "Error")
-    end
-  end
-end
diff --git a/lib/pleroma/web/websub/websub_server_subscription.ex b/lib/pleroma/web/websub/websub_server_subscription.ex
deleted file mode 100644 (file)
index d0ef548..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubServerSubscription do
-  use Ecto.Schema
-
-  schema "websub_server_subscriptions" do
-    field(:topic, :string)
-    field(:callback, :string)
-    field(:secret, :string)
-    field(:valid_until, :naive_datetime)
-    field(:state, :string)
-
-    timestamps()
-  end
-end
index 83d528a660f83d69dbba201366753c82f2318991..8ad756b625ec0c82d5a681b653f699d9f9d88ab6 100644 (file)
@@ -8,10 +8,6 @@ defmodule Pleroma.Workers.ReceiverWorker do
   use Pleroma.Workers.WorkerHelper, queue: "federator_incoming"
 
   @impl Oban.Worker
-  def perform(%{"op" => "incoming_doc", "body" => doc}, _job) do
-    Federator.perform(:incoming_doc, doc)
-  end
-
   def perform(%{"op" => "incoming_ap_doc", "params" => params}, _job) do
     Federator.perform(:incoming_ap_doc, params)
   end
diff --git a/lib/pleroma/workers/subscriber_worker.ex b/lib/pleroma/workers/subscriber_worker.ex
deleted file mode 100644 (file)
index fc490e3..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.SubscriberWorker do
-  alias Pleroma.Repo
-  alias Pleroma.Web.Federator
-  alias Pleroma.Web.Websub
-
-  use Pleroma.Workers.WorkerHelper, queue: "federator_outgoing"
-
-  @impl Oban.Worker
-  def perform(%{"op" => "refresh_subscriptions"}, _job) do
-    Federator.perform(:refresh_subscriptions)
-  end
-
-  def perform(%{"op" => "request_subscription", "websub_id" => websub_id}, _job) do
-    websub = Repo.get(Websub.WebsubClientSubscription, websub_id)
-    Federator.perform(:request_subscription, websub)
-  end
-
-  def perform(%{"op" => "verify_websub", "websub_id" => websub_id}, _job) do
-    websub = Repo.get(Websub.WebsubServerSubscription, websub_id)
-    Federator.perform(:verify_websub, websub)
-  end
-end
diff --git a/mix.exs b/mix.exs
index 6cf766c521cdbd109387b2b471b58b5c07c17397..120092f1bd7fef558e69a7b3e433bf6ca2ba8fc1 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -69,6 +69,7 @@ defmodule Pleroma.Mixfile do
   end
 
   # Specifies which paths to compile per environment.
+  defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
   defp elixirc_paths(:test), do: ["lib", "test/support"]
   defp elixirc_paths(_), do: ["lib"]
 
diff --git a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs
new file mode 100644 (file)
index 0000000..2f336a5
--- /dev/null
@@ -0,0 +1,22 @@
+defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do
+  use Ecto.Migration
+  alias Pleroma.User
+
+  def change do
+    execute("""
+    create or replace function safe_jsonb_set(target jsonb, path text[], new_value jsonb, create_missing boolean default true) returns jsonb as $$
+    declare
+      result jsonb;
+    begin
+      result := jsonb_set(target, path, coalesce(new_value, 'null'::jsonb), create_missing);
+      if result is NULL then
+        raise 'jsonb_set tried to wipe the object, please report this incindent to Pleroma bug tracker. https://git.pleroma.social/pleroma/pleroma/issues/new';
+        return target;
+      else
+        return result;
+      end if;
+    end;
+    $$ language plpgsql;
+    """)
+  end
+end
index bc4e828cc4a95253c9f7d962276ee0cd0d76a4cc..a5eec848b19b307e113f4874ea37f827c681ea94 100644 (file)
@@ -4,7 +4,7 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do
 
   def change do
     execute(
-      "update users set info = jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
+      "update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
     )
   end
 end
index 90f87a9909ed3960976019088645119c8958a917..9fc5b0bad8a0a64ec2ea9ace3887ff79ca95028c 100755 (executable)
@@ -141,8 +141,8 @@ else
 
        ACTION="$1"
        shift
-
-       if [ "$(echo \"$1\" | grep \"^-\" >/dev/null)" = false ]; then
+        echo "$1" | grep "^-" >/dev/null
+       if [ $? -eq 1 ]; then
                SUBACTION="$1"
                shift
        fi
index f430bdf75f5def543af0eab7a9cf9cceb4ae5cbf..a5af0d1b21c2a13f3286d12ac72a4e7b1bb58233 100644 (file)
@@ -23,6 +23,39 @@ defmodule Pleroma.Conversation.ParticipationTest do
     assert %Pleroma.Conversation{} = participation.conversation
   end
 
+  test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, _} =
+      CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: true}] = Participation.for_user(user)
+    [%{read: false} = participation] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 1
+
+    {:ok, _} =
+      CommonAPI.post(other_user, %{
+        "status" => "Hey @#{user.nickname}.",
+        "visibility" => "direct",
+        "in_reply_to_conversation_id" => participation.id
+      })
+
+    user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
+
+    [%{read: false}] = Participation.for_user(user)
+    [%{read: true}] = Participation.for_user(other_user)
+
+    assert User.get_cached_by_id(user.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 0
+  end
+
   test "for a new conversation, it sets the recipents of the participation" do
     user = insert(:user)
     other_user = insert(:user)
@@ -32,7 +65,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
       CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
 
     user = User.get_cached_by_id(user.id)
-    other_user = User.get_cached_by_id(user.id)
+    other_user = User.get_cached_by_id(other_user.id)
     [participation] = Participation.for_user(user)
     participation = Pleroma.Repo.preload(participation, :recipients)
 
diff --git a/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json b/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json
new file mode 100644 (file)
index 0000000..4b7b4df
--- /dev/null
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://shitposter.club/schemas/litepub-0.1.jsonld",{"@language":"und"}],"actor":"https://shitposter.club/users/moonman","attachment":[],"attributedTo":"https://shitposter.club/users/moonman","cc":["https://shitposter.club/users/moonman/followers"],"content":"@<a href=\"https://shitposter.club/users/9655\" class=\"h-card mention\" title=\"Solidarity for Pigs\">neimzr4luzerz</a> @<a href=\"https://gs.smuglo.li/user/2326\" class=\"h-card mention\" title=\"Dolus_McHonest\">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English","context":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","conversation":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","id":"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment","inReplyTo":"tag:shitposter.club,2017-05-05:noticeId=2827849:objectType=comment","inReplyToStatusId":2827849,"published":"2017-05-05T08:51:48Z","sensitive":false,"summary":null,"tag":[],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Note"}
\ No newline at end of file
diff --git a/test/fixtures/tesla_mock/moonman@shitposter.club.json b/test/fixtures/tesla_mock/moonman@shitposter.club.json
new file mode 100644 (file)
index 0000000..8f9ced1
--- /dev/null
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://shitposter.club/schemas/litepub-0.1.jsonld",{"@language":"und"}],"attachment":[],"endpoints":{"oauthAuthorizationEndpoint":"https://shitposter.club/oauth/authorize","oauthRegistrationEndpoint":"https://shitposter.club/api/v1/apps","oauthTokenEndpoint":"https://shitposter.club/oauth/token","sharedInbox":"https://shitposter.club/inbox"},"followers":"https://shitposter.club/users/moonman/followers","following":"https://shitposter.club/users/moonman/following","icon":{"type":"Image","url":"https://shitposter.club/media/bda6e00074f6a02cbf32ddb0abec08151eb4c795e580927ff7ad638d00cde4c8.jpg?name=blob.jpg"},"id":"https://shitposter.club/users/moonman","image":{"type":"Image","url":"https://shitposter.club/media/4eefb90d-cdb2-2b4f-5f29-7612856a99d2/4eefb90d-cdb2-2b4f-5f29-7612856a99d2.jpeg"},"inbox":"https://shitposter.club/users/moonman/inbox","manuallyApprovesFollowers":false,"name":"Captain Howdy","outbox":"https://shitposter.club/users/moonman/outbox","preferredUsername":"moonman","publicKey":{"id":"https://shitposter.club/users/moonman#main-key","owner":"https://shitposter.club/users/moonman","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOTitJ19ZqcOZHwSXQUM\nJq9ip4GNblp83LgwG1t5c2h2iaI3fXMsB4EaEBs8XHsoSFyDeDNRSPE3mtVgOnWv\n1eaXWMDerBT06th6DrElD9k5IoEPtZRY4HtZa1xGnte7+6RjuPOzZ1fR9C8WxGgi\nwb9iOUMhazpo85fC3iKCAL5XhiuA3Nas57MDJgueeI9BF+2oFelFZdMSWwG96uch\niDfp8nfpkmzYI6SWbylObjm8RsfZbGTosLHwWyJPEITeYI/5M0XwJe9dgVI1rVNU\n52kplWOGTo1rm6V0AMHaYAd9RpiXxe8xt5OeranrsE/5LvEQUl0fz7SE36YmsOaH\nTwIDAQAB\n-----END PUBLIC KEY-----\n\n"},"summary":"EMAIL:shitposterclub@gmail.com<br>XMPP: moon@talk.shitposter.club<br>PRONOUNS: none of your business<br><br>Purported leftist kike piece of shit","tag":[],"type":"Person","url":"https://shitposter.club/users/moonman"}
\ No newline at end of file
index a39a00e0221ff2b3e8265540da28ab53980ec4a1..81c0fef12766f3d0ef8f55e1dd8fea0206261bd6 100644 (file)
@@ -24,13 +24,13 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "delete"
         })
 
       log = Repo.one(ModerationLog)
 
-      assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}"
+      assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}"
     end
 
     test "logging user creation by moderator", %{
@@ -128,7 +128,7 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "grant",
           permission: "moderator"
         })
@@ -142,7 +142,7 @@ defmodule Pleroma.ModerationLogTest do
       {:ok, _} =
         ModerationLog.insert_log(%{
           actor: moderator,
-          subject: subject1,
+          subject: [subject1],
           action: "revoke",
           permission: "moderator"
         })
index 61cd1b41228d896e72f4596789e331ce3e2a2f66..0dc2728b9a0a44a70d7455171120d5f5a6a11ff2 100644 (file)
@@ -65,7 +65,7 @@ defmodule Pleroma.Object.ContainmentTest do
       assert capture_log(fn ->
                {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
              end) =~
-               "[error] Could not decode user at fetch https://n1u.moe/users/rye, {:error, :error}"
+               "[error] Could not decode user at fetch https://n1u.moe/users/rye"
     end
   end
 
index 895a73d2c893b7ad201fbe6bef1c26dd085ab6ee..9ae6b015d6f74ef3e4efa57a8ccd18034408dbed 100644 (file)
@@ -27,31 +27,16 @@ defmodule Pleroma.Object.FetcherTest do
   end
 
   describe "actor origin containment" do
-    test_with_mock "it rejects objects with a bogus origin",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects with a bogus origin" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
 
-    test_with_mock "it rejects objects when attributedTo is wrong (variant 1)",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects when attributedTo is wrong (variant 1)" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
 
-    test_with_mock "it rejects objects when attributedTo is wrong (variant 2)",
-                   Pleroma.Web.OStatus,
-                   [:passthrough],
-                   [] do
+    test "it rejects objects when attributedTo is wrong (variant 2)" do
       {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
-
-      refute called(Pleroma.Web.OStatus.fetch_activity_from_url(:_))
     end
   end
 
@@ -71,24 +56,6 @@ defmodule Pleroma.Object.FetcherTest do
 
       assert object == object_again
     end
-
-    test "it works with objects only available via Ostatus" do
-      {:ok, object} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
-      assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
-      assert activity.data["id"]
-
-      {:ok, object_again} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
-
-      assert object == object_again
-    end
-
-    test "it correctly stitches up conversations between ostatus and ap" do
-      last = "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
-      {:ok, object} = Fetcher.fetch_object_from_id(last)
-
-      object = Object.get_by_ap_id(object.data["inReplyTo"])
-      assert object
-    end
   end
 
   describe "implementation quirks" do
diff --git a/test/safe_jsonb_set_test.exs b/test/safe_jsonb_set_test.exs
new file mode 100644 (file)
index 0000000..7485405
--- /dev/null
@@ -0,0 +1,12 @@
+defmodule Pleroma.SafeJsonbSetTest do
+  use Pleroma.DataCase
+
+  test "it doesn't wipe the object when asked to set the value to NULL" do
+    assert %{rows: [[%{"key" => "value", "test" => nil}]]} =
+             Ecto.Adapters.SQL.query!(
+               Pleroma.Repo,
+               "select safe_jsonb_set('{\"key\": \"value\"}'::jsonb, '{test}', NULL);",
+               []
+             )
+  end
+end
index 96c8ba07ac213ab88dcef1a8611bbbeeacfc6c5b..6b168f2d993d48528b0ecd215b4f63824a6d591a 100644 (file)
@@ -69,8 +69,7 @@ defmodule Pleroma.SignatureTest do
 
     test "it returns error when not found user" do
       assert capture_log(fn ->
-               assert Signature.refetch_public_key(make_fake_conn("test-ap_id")) ==
-                        {:error, {:error, :ok}}
+               {:error, _} = Signature.refetch_public_key(make_fake_conn("test-ap_id"))
              end) =~ "[error] Could not decode user"
     end
   end
index b180844cd90acc7fde68ee3d95543bc89c2a1529..0fdb1e9521877ff5e1fb8be599a49a76738b6004 100644 (file)
@@ -281,26 +281,6 @@ defmodule Pleroma.Factory do
     }
   end
 
-  def websub_subscription_factory do
-    %Pleroma.Web.Websub.WebsubServerSubscription{
-      topic: "http://example.org",
-      callback: "http://example.org/callback",
-      secret: "here's a secret",
-      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 100),
-      state: "requested"
-    }
-  end
-
-  def websub_client_subscription_factory do
-    %Pleroma.Web.Websub.WebsubClientSubscription{
-      topic: "http://example.org",
-      secret: "here's a secret",
-      valid_until: nil,
-      state: "requested",
-      subscribers: []
-    }
-  end
-
   def oauth_app_factory do
     %Pleroma.Web.OAuth.App{
       client_name: "Some client",
index 4feb57f3ad84154251501f1538613bd8eaaaa0a2..7d65209fb1e30111595c947a83a459226158fdf4 100644 (file)
@@ -38,6 +38,14 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get("https://shitposter.club/users/moonman", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json")
+     }}
+  end
+
   def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _, _) do
     {:ok,
      %Tesla.Env{
@@ -620,7 +628,7 @@ defmodule HttpRequestMock do
     {:ok,
      %Tesla.Env{
        status: 200,
-       body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html")
+       body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json")
      }}
   end
 
index f7ab312872c0c820a8919dd318513e227c47d6db..78a02d5364c5434fc2dda5f3c83f5caa96512daf 100644 (file)
@@ -65,21 +65,6 @@ defmodule Pleroma.UserSearchTest do
       assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
     end
 
-    test "finds users, ranking by similarity" do
-      u1 = insert(:user, %{name: "lain"})
-      _u2 = insert(:user, %{name: "ean"})
-      u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
-      u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
-
-      assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
-    end
-
-    test "finds users, handling misspelled requests" do
-      u1 = insert(:user, %{name: "lain"})
-
-      assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
-    end
-
     test "finds users, boosting ranks of friends and followers" do
       u1 = insert(:user)
       u2 = insert(:user, %{name: "Doe"})
@@ -163,17 +148,6 @@ defmodule Pleroma.UserSearchTest do
       Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
     end
 
-    test "finds a user whose name is nil" do
-      _user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
-      user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
-
-      assert user_two ==
-               User.search("lain@pleroma.soykaf.com")
-               |> List.first()
-               |> Map.put(:search_rank, nil)
-               |> Map.put(:search_type, nil)
-    end
-
     test "does not yield false-positive matches" do
       insert(:user, %{name: "John Doe"})
 
index 019e7b400c32f4b2e5957fb303caa96f956c2eda..ad050b7da019cc77e7248cdad4d1a4090dd86d7e 100644 (file)
@@ -190,23 +190,6 @@ defmodule Pleroma.UserTest do
     refute User.following?(follower, followed)
   end
 
-  # This is a somewhat useless test.
-  # test "following a remote user will ensure a websub subscription is present" do
-  #   user = insert(:user)
-  #   {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
-
-  #   assert followed.local == false
-
-  #   {:ok, user} = User.follow(user, followed)
-  #   assert User.ap_followers(followed) in user.following
-
-  #   query = from w in WebsubClientSubscription,
-  #   where: w.topic == ^followed.info["topic"]
-  #   websub = Repo.one(query)
-
-  #   assert websub
-  # end
-
   describe "unfollow/2" do
     setup do
       setting = Pleroma.Config.get([:instance, :external_user_synchronization])
@@ -474,11 +457,6 @@ defmodule Pleroma.UserTest do
       assert user == fetched_user
     end
 
-    test "fetches an external user via ostatus if no user exists" do
-      {:ok, fetched_user} = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
-      assert fetched_user.nickname == "shp@social.heldscal.la"
-    end
-
     test "returns nil if no user could be fetched" do
       {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
       assert fetched_user == "not found nonexistant@social.heldscal.la"
index 3a5a2f9840a4674ef51560e19c11875c5d656df6..28a9b773c49f81a9050c81be13956dc90d04c7e2 100644 (file)
@@ -41,6 +41,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         assert called(Pleroma.Web.Streamer.stream("participation", participations))
       end
     end
+
+    test "streams them out on activity creation" do
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      with_mock Pleroma.Web.Streamer,
+        stream: fn _, _ -> nil end do
+        {:ok, activity} =
+          CommonAPI.post(user_one, %{
+            "status" => "@#{user_two.nickname}",
+            "visibility" => "direct"
+          })
+
+        conversation =
+          activity.data["context"]
+          |> Pleroma.Conversation.get_for_ap_id()
+          |> Repo.preload(participations: :user)
+
+        assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
+      end
+    end
   end
 
   describe "fetching restricted by visibility" do
index 0f7556538651c8c890d86595bb40502b2556f250..4a0a03944d1b8606325c4980c0e5a66bb93d66bc 100644 (file)
@@ -22,8 +22,8 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
   describe "follow/1" do
     test "returns errors when user not found" do
       assert capture_log(fn ->
-               assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"}
-             end) =~ "Could not fetch by AP id"
+               {:error, _} = Relay.follow("test-ap-id")
+             end) =~ "Could not decode user at fetch"
     end
 
     test "returns activity" do
@@ -41,8 +41,8 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
   describe "unfollow/1" do
     test "returns errors when user not found" do
       assert capture_log(fn ->
-               assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"}
-             end) =~ "Could not fetch by AP id"
+               {:error, _} = Relay.unfollow("test-ap-id")
+             end) =~ "Could not decode user at fetch"
     end
 
     test "returns activity" do
index 6c35a6f4d820f988a32c62f2a6a633836954ea4d..dbb6e59b0acc29ed74d34e665a6d65c87a5e938c 100644 (file)
@@ -7,14 +7,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.Object.Fetcher
-  alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.Websub.WebsubClientSubscription
 
   import Mock
   import Pleroma.Factory
@@ -1181,32 +1178,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert modified["object"]["actor"] == modified["object"]["attributedTo"]
     end
 
-    test "it translates ostatus IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
-    test "it translates ostatus reply_to IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity} =
-        CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
-
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
     test "it strips internal hashtag data" do
       user = insert(:user)
 
@@ -1371,21 +1342,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
     end
   end
 
-  describe "maybe_retire_websub" do
-    test "it deletes all websub client subscripitions with the user as topic" do
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
-      {:ok, ws} = Repo.insert(subscription)
-
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
-      {:ok, ws2} = Repo.insert(subscription)
-
-      Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
-
-      refute Repo.get(WebsubClientSubscription, ws.id)
-      assert Repo.get(WebsubClientSubscription, ws2.id)
-    end
-  end
-
   describe "actor rewriting" do
     test "it fixes the actor URL property to be a proper URI" do
       data = %{
index b5c355e66f3eb9c36f6ad41bf676306f8f518c26..9da4940be5132a2a0c44d8eb2e235094e852d5d0 100644 (file)
@@ -17,8 +17,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   alias Pleroma.Web.MediaProxy
   import Pleroma.Factory
 
-  describe "/api/pleroma/admin/users" do
-    test "Delete" do
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+    :ok
+  end
+
+  describe "DELETE /api/pleroma/admin/users" do
+    test "single user" do
       admin = insert(:user, info: %{is_admin: true})
       user = insert(:user)
 
@@ -30,15 +36,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       log_entry = Repo.one(ModerationLog)
 
-      assert log_entry.data["subject"]["nickname"] == user.nickname
-      assert log_entry.data["action"] == "delete"
-
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deleted user @#{user.nickname}"
+               "@#{admin.nickname} deleted users: @#{user.nickname}"
 
       assert json_response(conn, 200) == user.nickname
     end
 
+    test "multiple users" do
+      admin = insert(:user, info: %{is_admin: true})
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
+
+      response = json_response(conn, 200)
+      assert response -- [user_one.nickname, user_two.nickname] == []
+    end
+  end
+
+  describe "/api/pleroma/admin/users" do
     test "Create" do
       admin = insert(:user, info: %{is_admin: true})
 
@@ -404,82 +431,72 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "@#{admin.nickname} made @#{user.nickname} admin"
     end
 
-    test "/:right DELETE, can remove from a permission group" do
+    test "/:right POST, can add to a permission group (multiple)" do
       admin = insert(:user, info: %{is_admin: true})
-      user = insert(:user, info: %{is_admin: true})
+      user_one = insert(:user)
+      user_two = insert(:user)
 
       conn =
         build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
-        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
+        |> post("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
       assert json_response(conn, 200) == %{
-               "is_admin" => false
+               "is_admin" => true
              }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
+               "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
     end
-  end
 
-  describe "PUT /api/pleroma/admin/users/:nickname/activation_status" do
-    setup %{conn: conn} do
+    test "/:right DELETE, can remove from a permission group" do
       admin = insert(:user, info: %{is_admin: true})
+      user = insert(:user, info: %{is_admin: true})
 
       conn =
-        conn
+        build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
 
-      %{conn: conn, admin: admin}
-    end
-
-    test "deactivates the user", %{conn: conn, admin: admin} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == true
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deactivated user @#{user.nickname}"
+               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
     end
 
-    test "activates the user", %{conn: conn, admin: admin} do
-      user = insert(:user, info: %{deactivated: true})
+    test "/:right DELETE, can remove from a permission group (multiple)" do
+      admin = insert(:user, info: %{is_admin: true})
+      user_one = insert(:user, info: %{is_admin: true})
+      user_two = insert(:user, info: %{is_admin: true})
 
       conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: true})
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == false
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} activated user @#{user.nickname}"
-    end
-
-    test "returns 403 when requested by a non-admin", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> assign(:user, user)
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      assert json_response(conn, :forbidden)
+               "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
+                 user_two.nickname
+               }"
     end
   end
 
@@ -1029,6 +1046,50 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
+  test "PATCH /api/pleroma/admin/users/activate" do
+    admin = insert(:user, info: %{is_admin: true})
+    user_one = insert(:user, info: %{deactivated: true})
+    user_two = insert(:user, info: %{deactivated: true})
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/activate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/deactivate" do
+    admin = insert(:user, info: %{is_admin: true})
+    user_one = insert(:user, info: %{deactivated: false})
+    user_two = insert(:user, info: %{deactivated: false})
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/deactivate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
     admin = insert(:user, info: %{is_admin: true})
     user = insert(:user)
@@ -1053,7 +1114,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     log_entry = Repo.one(ModerationLog)
 
     assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} deactivated user @#{user.nickname}"
+             "@#{admin.nickname} deactivated users: @#{user.nickname}"
   end
 
   describe "POST /api/pleroma/admin/users/invite_token" do
@@ -2486,6 +2547,74 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert User.get_by_id(user.id).info.password_reset_pending == true
     end
   end
+
+  describe "relays" do
+    setup %{conn: conn} do
+      admin = insert(:user, info: %{is_admin: true})
+
+      %{conn: assign(conn, :user, admin), admin: admin}
+    end
+
+    test "POST /relay", %{admin: admin} do
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> post("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+    end
+
+    test "GET /relay", %{admin: admin} do
+      Pleroma.Web.ActivityPub.Relay.get_actor()
+      |> Ecto.Changeset.change(
+        following: [
+          "http://test-app.com/user/test1",
+          "http://test-app.com/user/test1",
+          "http://test-app-42.com/user/test1"
+        ]
+      )
+      |> Pleroma.User.update_and_set_cache()
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/relay")
+
+      assert json_response(conn, 200)["relays"] -- ["test-app.com", "test-app-42.com"] == []
+    end
+
+    test "DELETE /relay", %{admin: admin} do
+      build_conn()
+      |> assign(:user, admin)
+      |> post("/api/pleroma/admin/relay", %{
+        relay_url: "http://mastodon.example.org/users/admin"
+      })
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> delete("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry_one) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+
+      assert ModerationLog.get_log_entry_message(log_entry_two) ==
+               "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
+    end
+  end
 end
 
 # Needed for testing
index 43a715706fd06424575a795b7ace49c641cf768f..bdaefdce13844bec83a9e75d3eaf4a1308c5ceaf 100644 (file)
@@ -111,93 +111,6 @@ defmodule Pleroma.Web.FederatorTest do
                all_enqueued(worker: PublisherWorker)
              )
     end
-
-    test "it federates only to reachable instances via Websub" do
-      user = insert(:user)
-      websub_topic = Pleroma.Web.OStatus.feed_path(user)
-
-      sub1 =
-        insert(:websub_subscription, %{
-          topic: websub_topic,
-          state: "active",
-          callback: "http://pleroma.soykaf.com/cb"
-        })
-
-      sub2 =
-        insert(:websub_subscription, %{
-          topic: websub_topic,
-          state: "active",
-          callback: "https://pleroma2.soykaf.com/cb"
-        })
-
-      dt = NaiveDateTime.utc_now()
-      Instances.set_unreachable(sub2.callback, dt)
-
-      Instances.set_consistently_unreachable(sub1.callback)
-
-      {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI"})
-
-      expected_callback = sub2.callback
-      expected_dt = NaiveDateTime.to_iso8601(dt)
-
-      ObanHelpers.perform(all_enqueued(worker: PublisherWorker))
-
-      assert ObanHelpers.member?(
-               %{
-                 "op" => "publish_one",
-                 "params" => %{
-                   "callback" => expected_callback,
-                   "unreachable_since" => expected_dt
-                 }
-               },
-               all_enqueued(worker: PublisherWorker)
-             )
-    end
-
-    test "it federates only to reachable instances via Salmon" do
-      user = insert(:user)
-
-      _remote_user1 =
-        insert(:user, %{
-          local: false,
-          nickname: "nick1@domain.com",
-          ap_id: "https://domain.com/users/nick1",
-          info: %{salmon: "https://domain.com/salmon"}
-        })
-
-      remote_user2 =
-        insert(:user, %{
-          local: false,
-          nickname: "nick2@domain2.com",
-          ap_id: "https://domain2.com/users/nick2",
-          info: %{salmon: "https://domain2.com/salmon"}
-        })
-
-      remote_user2_id = remote_user2.id
-
-      dt = NaiveDateTime.utc_now()
-      Instances.set_unreachable(remote_user2.ap_id, dt)
-
-      Instances.set_consistently_unreachable("domain.com")
-
-      {:ok, _activity} =
-        CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"})
-
-      expected_dt = NaiveDateTime.to_iso8601(dt)
-
-      ObanHelpers.perform(all_enqueued(worker: PublisherWorker))
-
-      assert ObanHelpers.member?(
-               %{
-                 "op" => "publish_one",
-                 "params" => %{
-                   "recipient_id" => remote_user2_id,
-                   "unreachable_since" => expected_dt
-                 }
-               },
-               all_enqueued(worker: PublisherWorker)
-             )
-    end
   end
 
   describe "Receive an activity" do
index a308a76201e216edb106cb92fb7b4ee971f41f61..d89a8717930083787f166365b6f30e68d68983e8 100644 (file)
@@ -54,9 +54,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
     assert user_two.id in account_ids
     assert user_three.id in account_ids
     assert is_binary(res_id)
-    assert unread == true
+    assert unread == false
     assert res_last_status["id"] == direct.id
-    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
   end
 
   test "updates the last_status on reply", %{conn: conn} do
@@ -95,19 +95,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         "visibility" => "direct"
       })
 
+    assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1
+
     [%{"id" => direct_conversation_id, "unread" => true}] =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> get("/api/v1/conversations")
       |> json_response(200)
 
     %{"unread" => false} =
       conn
-      |> assign(:user, user_one)
+      |> assign(:user, user_two)
       |> post("/api/v1/conversations/#{direct_conversation_id}/read")
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
 
     # The conversation is marked as unread on reply
     {:ok, _} =
@@ -124,6 +128,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       |> json_response(200)
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
 
     # A reply doesn't increment the user's unread_conversation_count if the conversation is unread
     {:ok, _} =
@@ -134,6 +139,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       })
 
     assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1
+    assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0
   end
 
   test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do
index ee413eef7c8219a4f132f7df794db8397f185a29..7953fad62c197ada25def63fd8821988c7a19184 100644 (file)
@@ -204,17 +204,17 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
       conn =
         conn
         |> assign(:user, user)
-        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
+        |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"})
 
       assert results = json_response(conn, 200)
       [account] = results["accounts"]
-      assert account["acct"] == "shp@social.heldscal.la"
+      assert account["acct"] == "mike@osada.macgirvin.com"
     end
 
     test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
       conn =
         conn
-        |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
+        |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "false"})
 
       assert results = json_response(conn, 200)
       assert [] == results["accounts"]
index fc45c25de98c893a7f31ca67282463348064c43e..61b6cea7547c49ec2d90a115b8cf2e4abd248e3f 100644 (file)
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
   alias Pleroma.Config
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus
 
   clear_config([:instance, :public])
 
@@ -75,8 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
 
       {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
 
-      {:ok, [_activity]} =
-        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
+      _activity = insert(:note_activity, local: false)
 
       conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"})
 
@@ -271,9 +269,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
 
       {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
 
-      {:ok, [_activity]} =
-        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
-
       nconn = get(conn, "/api/v1/timelines/tag/2hu")
 
       assert [%{"id" => id}] = json_response(nconn, :ok)
index b7a4938a65996df0ea312bdd9381817aed0dc7b2..ad209b4a34d8d20e1e24fa502d9ba2a67a4f55e0 100644 (file)
@@ -424,8 +424,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
       other_user = insert(:user)
 
       {:ok, _activity} =
-        CommonAPI.post(user, %{
-          "status" => "Hey @#{other_user.nickname}.",
+        CommonAPI.post(other_user, %{
+          "status" => "Hey @#{user.nickname}.",
           "visibility" => "direct"
         })
 
index 1d5a6e956db95d7be1edbc792c032f2e59145d92..c200ad8feb6d353cb1499da5e5060581c6f49a5d 100644 (file)
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.StatusView
-  alias Pleroma.Web.OStatus
   import Pleroma.Factory
   import Tesla.Mock
 
@@ -230,17 +229,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
   end
 
   test "contains mentions" do
-    incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
-    # a user with this ap id might be in the cache.
-    recipient = "https://pleroma.soykaf.com/users/lain"
-    user = insert(:user, %{ap_id: recipient})
+    user = insert(:user)
+    mentioned = insert(:user)
 
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
+    {:ok, activity} = CommonAPI.post(user, %{"status" => "hi @#{mentioned.nickname}"})
 
     status = StatusView.render("show.json", %{activity: activity})
 
     assert status.mentions ==
-             Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end)
+             Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
   end
 
   test "create mentions from the 'to' field" do
diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs
deleted file mode 100644 (file)
index a8d5008..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
-  use Pleroma.DataCase
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.User
-  alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "an external note activity" do
-    incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-
-    tuple = ActivityRepresenter.to_simple_form(activity, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert String.contains?(
-             res,
-             ~s{<link type="text/html" href="https://mastodon.social/users/lambadalambda/updates/2314748" rel="alternate"/>}
-           )
-  end
-
-  test "a note activity" do
-    note_activity = insert(:note_activity)
-    object_data = Object.normalize(note_activity).data
-
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <id>#{object_data["id"]}</id>
-    <title>New note by #{user.nickname}</title>
-    <content type="html">#{object_data["content"]}</content>
-    <published>#{object_data["published"]}</published>
-    <updated>#{object_data["published"]}</updated>
-    <ostatus:conversation ref="#{note_activity.data["context"]}">#{note_activity.data["context"]}</ostatus:conversation>
-    <link ref="#{note_activity.data["context"]}" rel="ostatus:conversation" />
-    <summary>#{object_data["summary"]}</summary>
-    <link type="application/atom+xml" href="#{object_data["id"]}" rel="self" />
-    <link type="text/html" href="#{object_data["id"]}" rel="alternate" />
-    <category term="2hu"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    <link name="2hu" rel="emoji" href="corndog.png" />
-    """
-
-    tuple = ActivityRepresenter.to_simple_form(note_activity, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a reply note" do
-    user = insert(:user)
-    note_object = insert(:note)
-    _note = insert(:note_activity, %{note: note_object})
-    object = insert(:note, %{data: %{"inReplyTo" => note_object.data["id"]}})
-    answer = insert(:note_activity, %{note: object})
-
-    Repo.update!(
-      Object.change(note_object, %{data: Map.put(note_object.data, "external_url", "someurl")})
-    )
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <id>#{object.data["id"]}</id>
-    <title>New note by #{user.nickname}</title>
-    <content type="html">#{object.data["content"]}</content>
-    <published>#{object.data["published"]}</published>
-    <updated>#{object.data["published"]}</updated>
-    <ostatus:conversation ref="#{answer.data["context"]}">#{answer.data["context"]}</ostatus:conversation>
-    <link ref="#{answer.data["context"]}" rel="ostatus:conversation" />
-    <summary>2hu</summary>
-    <link type="application/atom+xml" href="#{object.data["id"]}" rel="self" />
-    <link type="text/html" href="#{object.data["id"]}" rel="alternate" />
-    <category term="2hu"/>
-    <thr:in-reply-to ref="#{note_object.data["id"]}" href="someurl" />
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    <link name="2hu" rel="emoji" href="corndog.png" />
-    """
-
-    tuple = ActivityRepresenter.to_simple_form(answer, user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an announce activity" do
-    note = insert(:note_activity)
-    user = insert(:user)
-    object = Object.normalize(note)
-
-    {:ok, announce, _object} = ActivityPub.announce(user, object)
-
-    announce = Activity.get_by_id(announce.id)
-
-    note_user = User.get_cached_by_ap_id(note.data["actor"])
-    note = Activity.get_by_id(note.id)
-
-    note_xml =
-      ActivityRepresenter.to_simple_form(note, note_user, true)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-      |> to_string
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
-    <id>#{announce.data["id"]}</id>
-    <title>#{user.nickname} repeated a notice</title>
-    <content type="html">RT #{object.data["content"]}</content>
-    <published>#{announce.data["published"]}</published>
-    <updated>#{announce.data["published"]}</updated>
-    <ostatus:conversation ref="#{announce.data["context"]}">#{announce.data["context"]}</ostatus:conversation>
-    <link ref="#{announce.data["context"]}" rel="ostatus:conversation" />
-    <link rel="self" type="application/atom+xml" href="#{announce.data["id"]}"/>
-    <activity:object>
-      #{note_xml}
-    </activity:object>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      note.data["actor"]
-    }"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    """
-
-    announce_xml =
-      ActivityRepresenter.to_simple_form(announce, user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-      |> to_string
-
-    assert clean(expected) == clean(announce_xml)
-  end
-
-  test "a like activity" do
-    note = insert(:note)
-    user = insert(:user)
-    {:ok, like, _note} = ActivityPub.like(user, note)
-
-    tuple = ActivityRepresenter.to_simple_form(like, user)
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb>
-    <id>#{like.data["id"]}</id>
-    <title>New favorite by #{user.nickname}</title>
-    <content type="html">#{user.nickname} favorited something</content>
-    <published>#{like.data["published"]}</published>
-    <updated>#{like.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-      <id>#{note.data["id"]}</id>
-    </activity:object>
-    <ostatus:conversation ref="#{like.data["context"]}">#{like.data["context"]}</ostatus:conversation>
-    <link ref="#{like.data["context"]}" rel="ostatus:conversation" />
-    <link rel="self" type="application/atom+xml" href="#{like.data["id"]}"/>
-    <thr:in-reply-to ref="#{note.data["id"]}" />
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      note.data["actor"]
-    }"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a follow activity" do
-    follower = insert(:user)
-    followed = insert(:user)
-
-    {:ok, activity} =
-      ActivityPub.insert(%{
-        "type" => "Follow",
-        "actor" => follower.ap_id,
-        "object" => followed.ap_id,
-        "to" => [followed.ap_id]
-      })
-
-    tuple = ActivityRepresenter.to_simple_form(activity, follower)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/follow</activity:verb>
-    <id>#{activity.data["id"]}</id>
-    <title>#{follower.nickname} started following #{activity.data["object"]}</title>
-    <content type="html"> #{follower.nickname} started following #{activity.data["object"]}</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <id>#{activity.data["object"]}</id>
-      <uri>#{activity.data["object"]}</uri>
-    </activity:object>
-    <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      activity.data["object"]
-    }"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an unfollow activity" do
-    follower = insert(:user)
-    followed = insert(:user)
-    {:ok, _activity} = ActivityPub.follow(follower, followed)
-    {:ok, activity} = ActivityPub.unfollow(follower, followed)
-
-    tuple = ActivityRepresenter.to_simple_form(activity, follower)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/unfollow</activity:verb>
-    <id>#{activity.data["id"]}</id>
-    <title>#{follower.nickname} stopped following #{followed.ap_id}</title>
-    <content type="html"> #{follower.nickname} stopped following #{followed.ap_id}</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    <activity:object>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <id>#{followed.ap_id}</id>
-      <uri>#{followed.ap_id}</uri>
-    </activity:object>
-    <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
-    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
-      followed.ap_id
-    }"/>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "a delete" do
-    user = insert(:user)
-
-    activity = %Activity{
-      data: %{
-        "id" => "ap_id",
-        "type" => "Delete",
-        "actor" => user.ap_id,
-        "object" => "some_id",
-        "published" => "2017-06-18T12:00:18+00:00"
-      }
-    }
-
-    tuple = ActivityRepresenter.to_simple_form(activity, nil)
-
-    refute is_nil(tuple)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary()
-
-    expected = """
-    <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
-    <id>#{activity.data["object"]}</id>
-    <title>An object was deleted</title>
-    <content type="html">An object was deleted</content>
-    <published>#{activity.data["published"]}</published>
-    <updated>#{activity.data["published"]}</updated>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  test "an unknown activity" do
-    tuple = ActivityRepresenter.to_simple_form(%Activity{}, nil)
-    assert is_nil(tuple)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
diff --git a/test/web/ostatus/feed_representer_test.exs b/test/web/ostatus/feed_representer_test.exs
deleted file mode 100644 (file)
index d1cadf1..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.FeedRepresenterTest do
-  use Pleroma.DataCase
-  import Pleroma.Factory
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.OStatus.ActivityRepresenter
-  alias Pleroma.Web.OStatus.FeedRepresenter
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  test "returns a feed of the last 20 items of the user" do
-    note_activity = insert(:note_activity)
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user])
-
-    most_recent_update =
-      note_activity.updated_at
-      |> NaiveDateTime.to_iso8601()
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string
-
-    user_xml =
-      UserRepresenter.to_simple_form(user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-
-    entry_xml =
-      ActivityRepresenter.to_simple_form(note_activity, user)
-      |> :xmerl.export_simple_content(:xmerl_xml)
-
-    expected = """
-    <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0">
-      <id>#{OStatus.feed_path(user)}</id>
-      <title>#{user.nickname}'s timeline</title>
-      <updated>#{most_recent_update}</updated>
-      <logo>#{User.avatar_url(user)}</logo>
-      <link rel="hub" href="#{OStatus.pubsub_path(user)}" />
-      <link rel="salmon" href="#{OStatus.salmon_path(user)}" />
-      <link rel="self" href="#{OStatus.feed_path(user)}" type="application/atom+xml" />
-      <author>
-        #{user_xml}
-      </author>
-      <link rel="next" href="#{OStatus.feed_path(user)}?max_id=#{note_activity.id}" type="application/atom+xml" />
-      <entry>
-        #{entry_xml}
-      </entry>
-    </feed>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs
deleted file mode 100644 (file)
index cd0447a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
-  use Pleroma.DataCase
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.Web.OStatus
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  describe "deletions" do
-    test "it removes the mentioned activity" do
-      note = insert(:note_activity)
-      second_note = insert(:note_activity)
-      object = Object.normalize(note)
-      second_object = Object.normalize(second_note)
-      user = insert(:user)
-
-      {:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object)
-
-      incoming =
-        File.read!("test/fixtures/delete.xml")
-        |> String.replace(
-          "tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status",
-          object.data["id"]
-        )
-
-      {:ok, [delete]} = OStatus.handle_incoming(incoming)
-
-      refute Activity.get_by_id(note.id)
-      refute Activity.get_by_id(like.id)
-      assert Object.get_by_ap_id(object.data["id"]).data["type"] == "Tombstone"
-      assert Activity.get_by_id(second_note.id)
-      assert Object.get_by_ap_id(second_object.data["id"])
-
-      assert delete.data["type"] == "Delete"
-    end
-  end
-end
index b1af918d8792c8ce76076948c0e3ddac50e20287..58534396e9810204921b4f1c62ad258b8b6d3424 100644 (file)
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
   alias Pleroma.Object
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus.ActivityRepresenter
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -22,78 +21,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
     Pleroma.Config.put([:instance, :federating], true)
   end
 
-  describe "salmon_incoming" do
-    test "decodes a salmon", %{conn: conn} do
-      user = insert(:user)
-      salmon = File.read!("test/fixtures/salmon.xml")
-
-      assert capture_log(fn ->
-               conn =
-                 conn
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-    end
-
-    test "decodes a salmon with a changed magic key", %{conn: conn} do
-      user = insert(:user)
-      salmon = File.read!("test/fixtures/salmon.xml")
-
-      assert capture_log(fn ->
-               conn =
-                 conn
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-
-      # Wrong key
-      info = %{
-        magic_key:
-          "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
-      }
-
-      # Set a wrong magic-key for a user so it has to refetch
-      "http://gs.example.org:4040/index.php/user/1"
-      |> User.get_cached_by_ap_id()
-      |> User.update_info(&User.Info.remote_user_creation(&1, info))
-
-      assert capture_log(fn ->
-               conn =
-                 build_conn()
-                 |> put_req_header("content-type", "application/atom+xml")
-                 |> post("/users/#{user.nickname}/salmon", salmon)
-
-               assert response(conn, 200)
-             end) =~ "[error]"
-    end
-  end
-
   describe "GET object/2" do
-    test "gets an object", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      object = Object.normalize(note_activity)
-      user = User.get_cached_by_ap_id(note_activity.data["actor"])
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-      url = "/objects/#{uuid}"
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/xml")
-        |> get(url)
-
-      expected =
-        ActivityRepresenter.to_simple_form(note_activity, user, true)
-        |> ActivityRepresenter.wrap_with_entry()
-        |> :xmerl.export_simple(:xmerl_xml)
-        |> to_string
-
-      assert response(conn, 200) == expected
-    end
-
     test "redirects to /notice/id for html format", %{conn: conn} do
       note_activity = insert(:note_activity)
       object = Object.normalize(note_activity)
@@ -143,16 +71,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
   end
 
   describe "GET activity/2" do
-    test "gets an activity in xml format", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/activities/#{uuid}")
-      |> response(200)
-    end
-
     test "redirects to /notice/id for html format", %{conn: conn} do
       note_activity = insert(:note_activity)
       [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
@@ -180,24 +98,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
       assert response(conn, 500) == ~S({"error":"Something went wrong"})
     end
 
-    test "404s on deleted objects", %{conn: conn} do
-      note_activity = insert(:note_activity)
-      object = Object.normalize(note_activity)
-      [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/objects/#{uuid}")
-      |> response(200)
-
-      Object.delete(object)
-
-      conn
-      |> put_req_header("accept", "application/xml")
-      |> get("/objects/#{uuid}")
-      |> response(404)
-    end
-
     test "404s on private activities", %{conn: conn} do
       note_activity = insert(:direct_note_activity)
       [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
deleted file mode 100644 (file)
index 70a0e44..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatusTest do
-  use Pleroma.DataCase
-  alias Pleroma.Activity
-  alias Pleroma.Instances
-  alias Pleroma.Object
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.XML
-
-  import ExUnit.CaptureLog
-  import Mock
-  import Pleroma.Factory
-
-  setup_all do
-    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "don't insert create notes twice" do
-    incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    assert {:ok, [activity]} == OStatus.handle_incoming(incoming)
-  end
-
-  test "handle incoming note - GS, Salmon" do
-    incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    assert user.info.note_count == 1
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-
-    assert object.data["id"] == "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note"
-
-    assert activity.data["published"] == "2017-04-23T14:51:03+00:00"
-    assert object.data["published"] == "2017-04-23T14:51:03+00:00"
-
-    assert activity.data["context"] ==
-             "tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b"
-
-    assert "http://pleroma.example.org:4000/users/lain3" in activity.data["to"]
-    assert object.data["emoji"] == %{"marko" => "marko.png", "reimu" => "reimu.png"}
-    assert activity.local == false
-  end
-
-  test "handle incoming notes - GS, subscription" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_post.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert object.data["content"] == "Will it blend?"
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    assert User.ap_followers(user) in activity.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes with attachments - GS, subscription" do
-    incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert object.data["attachment"] |> length == 2
-    assert object.data["external_url"] == "https://social.heldscal.la/notice/2020923"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes with tags" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert object.data["tag"] == ["nsfw"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes - Mastodon, salmon, reply" do
-    # It uses the context of the replied to object
-    Repo.insert!(%Object{
-      data: %{
-        "id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4",
-        "context" => "2hu"
-      }
-    })
-
-    incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert activity.data["context"] == "2hu"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming notes - Mastodon, with CW" do
-    incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert object.data["summary"] == "technologic"
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming unlisted messages, put public into cc" do
-    incoming = File.read!("test/fixtures/mastodon-note-unlisted.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["cc"]
-    refute "https://www.w3.org/ns/activitystreams#Public" in object.data["to"]
-    assert "https://www.w3.org/ns/activitystreams#Public" in object.data["cc"]
-  end
-
-  test "handle incoming retweets - Mastodon, with CW" do
-    incoming = File.read!("test/fixtures/cw_retweet.xml")
-    {:ok, [[_activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity)
-
-    assert retweeted_object.data["summary"] == "Hey."
-  end
-
-  test "handle incoming notes - GS, subscription, reply" do
-    incoming = File.read!("test/fixtures/ostatus_incoming_reply.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-    assert object.data["actor"] == "https://social.heldscal.la/user/23211"
-
-    assert object.data["content"] ==
-             "@<a href=\"https://gs.archae.me/user/4687\" class=\"h-card u-url p-nickname mention\" title=\"shpbot\">shpbot</a> why not indeed."
-
-    assert object.data["inReplyTo"] ==
-             "tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note"
-
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-  end
-
-  test "handle incoming retweets - GS, subscription" do
-    incoming = File.read!("test/fixtures/share-gs.xml")
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == retweeted_activity.data["object"]
-    assert "https://pleroma.soykaf.com/users/lain" in activity.data["to"]
-    refute activity.local
-
-    retweeted_activity = Activity.get_by_id(retweeted_activity.id)
-    retweeted_object = Object.normalize(retweeted_activity)
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
-    refute retweeted_activity.local
-    assert retweeted_object.data["announcement_count"] == 1
-    assert String.contains?(retweeted_object.data["content"], "mastodon")
-    refute String.contains?(retweeted_object.data["content"], "Test account")
-  end
-
-  test "handle incoming retweets - GS, subscription - local message" do
-    incoming = File.read!("test/fixtures/share-gs-local.xml")
-    note_activity = insert(:note_activity)
-    object = Object.normalize(note_activity)
-    user = User.get_cached_by_ap_id(note_activity.data["actor"])
-
-    incoming =
-      incoming
-      |> String.replace("LOCAL_ID", object.data["id"])
-      |> String.replace("LOCAL_USER", user.ap_id)
-
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == object.data["id"]
-    assert user.ap_id in activity.data["to"]
-    refute activity.local
-
-    retweeted_activity = Activity.get_by_id(retweeted_activity.id)
-    assert note_activity.id == retweeted_activity.id
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == user.ap_id
-    assert retweeted_activity.local
-    assert Object.normalize(retweeted_activity).data["announcement_count"] == 1
-  end
-
-  test "handle incoming retweets - Mastodon, salmon" do
-    incoming = File.read!("test/fixtures/share.xml")
-    {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
-    retweeted_object = Object.normalize(retweeted_activity)
-
-    assert activity.data["type"] == "Announce"
-    assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
-    assert activity.data["object"] == retweeted_activity.data["object"]
-
-    assert activity.data["id"] ==
-             "tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status"
-
-    refute activity.local
-    assert retweeted_activity.data["type"] == "Create"
-    assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
-    refute retweeted_activity.local
-    refute String.contains?(retweeted_object.data["content"], "Test account")
-  end
-
-  test "handle incoming favorites - GS, websub" do
-    capture_log(fn ->
-      incoming = File.read!("test/fixtures/favorite.xml")
-      {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
-
-      assert activity.data["type"] == "Like"
-      assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-      assert activity.data["object"] == favorited_activity.data["object"]
-
-      assert activity.data["id"] ==
-               "tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00"
-
-      refute activity.local
-      assert favorited_activity.data["type"] == "Create"
-      assert favorited_activity.data["actor"] == "https://shitposter.club/user/1"
-
-      assert favorited_activity.data["object"] ==
-               "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
-
-      refute favorited_activity.local
-    end)
-  end
-
-  test "handle conversation references" do
-    incoming = File.read!("test/fixtures/mastodon_conversation.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["context"] ==
-             "tag:mastodon.social,2017-08-28:objectId=7876885:objectType=Conversation"
-  end
-
-  test "handle incoming favorites with locally available object - GS, websub" do
-    note_activity = insert(:note_activity)
-    object = Object.normalize(note_activity)
-
-    incoming =
-      File.read!("test/fixtures/favorite_with_local_note.xml")
-      |> String.replace("localid", object.data["id"])
-
-    {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Like"
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == object.data["id"]
-    refute activity.local
-    assert note_activity.id == favorited_activity.id
-    assert favorited_activity.local
-  end
-
-  test_with_mock "handle incoming replies, fetching replied-to activities if we don't have them",
-                 OStatus,
-                 [:passthrough],
-                 [] do
-    incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    object = Object.normalize(activity, false)
-
-    assert activity.data["type"] == "Create"
-    assert object.data["type"] == "Note"
-
-    assert object.data["inReplyTo"] ==
-             "http://pleroma.example.org:4000/objects/55bce8fc-b423-46b1-af71-3759ab4670bc"
-
-    assert "http://pleroma.example.org:4000/users/lain5" in activity.data["to"]
-
-    assert object.data["id"] == "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note"
-
-    assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
-
-    assert called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
-  end
-
-  test_with_mock "handle incoming replies, not fetching replied-to activities beyond max_replies_depth",
-                 OStatus,
-                 [:passthrough],
-                 [] do
-    incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
-
-    with_mock Pleroma.Web.Federator,
-      allowed_incoming_reply_depth?: fn _ -> false end do
-      {:ok, [activity]} = OStatus.handle_incoming(incoming)
-      object = Object.normalize(activity, false)
-
-      refute called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
-    end
-  end
-
-  test "handle incoming follows" do
-    incoming = File.read!("test/fixtures/follow.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-    assert activity.data["type"] == "Follow"
-
-    assert activity.data["id"] ==
-             "tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
-
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    assert activity.data["object"] == "https://pawoo.net/users/pekorino"
-    refute activity.local
-
-    follower = User.get_cached_by_ap_id(activity.data["actor"])
-    followed = User.get_cached_by_ap_id(activity.data["object"])
-
-    assert User.following?(follower, followed)
-  end
-
-  test "refuse following over OStatus if the followed's account is locked" do
-    incoming = File.read!("test/fixtures/follow.xml")
-    _user = insert(:user, info: %{locked: true}, ap_id: "https://pawoo.net/users/pekorino")
-
-    {:ok, [{:error, "It's not possible to follow locked accounts over OStatus"}]} =
-      OStatus.handle_incoming(incoming)
-  end
-
-  test "handle incoming unfollows with existing follow" do
-    incoming_follow = File.read!("test/fixtures/follow.xml")
-    {:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
-
-    incoming = File.read!("test/fixtures/unfollow.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["type"] == "Undo"
-
-    assert activity.data["id"] ==
-             "undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00"
-
-    assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
-    embedded_object = activity.data["object"]
-    assert is_map(embedded_object)
-    assert embedded_object["type"] == "Follow"
-    assert embedded_object["object"] == "https://pawoo.net/users/pekorino"
-    refute activity.local
-
-    follower = User.get_cached_by_ap_id(activity.data["actor"])
-    followed = User.get_cached_by_ap_id(embedded_object["object"])
-
-    refute User.following?(follower, followed)
-  end
-
-  test "it clears `unreachable` federation status of the sender" do
-    incoming_reaction_xml = File.read!("test/fixtures/share-gs.xml")
-    doc = XML.parse_document(incoming_reaction_xml)
-    actor_uri = XML.string_from_xpath("//author/uri[1]", doc)
-    reacted_to_author_uri = XML.string_from_xpath("//author/uri[2]", doc)
-
-    Instances.set_consistently_unreachable(actor_uri)
-    Instances.set_consistently_unreachable(reacted_to_author_uri)
-    refute Instances.reachable?(actor_uri)
-    refute Instances.reachable?(reacted_to_author_uri)
-
-    {:ok, _} = OStatus.handle_incoming(incoming_reaction_xml)
-    assert Instances.reachable?(actor_uri)
-    refute Instances.reachable?(reacted_to_author_uri)
-  end
-
-  describe "new remote user creation" do
-    test "returns local users" do
-      local_user = insert(:user)
-      {:ok, user} = OStatus.find_or_make_user(local_user.ap_id)
-
-      assert user == local_user
-    end
-
-    test "tries to use the information in poco fields" do
-      uri = "https://social.heldscal.la/user/23211"
-
-      {:ok, user} = OStatus.find_or_make_user(uri)
-
-      user = User.get_cached_by_id(user.id)
-      assert user.name == "Constance Variable"
-      assert user.nickname == "lambadalambda@social.heldscal.la"
-      assert user.local == false
-      assert user.info.uri == uri
-      assert user.ap_id == uri
-      assert user.bio == "Call me Deacon Blues."
-      assert user.avatar["type"] == "Image"
-
-      {:ok, user_again} = OStatus.find_or_make_user(uri)
-
-      assert user == user_again
-    end
-
-    test "find_or_make_user sets all the nessary input fields" do
-      uri = "https://social.heldscal.la/user/23211"
-      {:ok, user} = OStatus.find_or_make_user(uri)
-
-      assert user.info ==
-               %User.Info{
-                 id: user.info.id,
-                 ap_enabled: false,
-                 background: %{},
-                 banner: %{},
-                 blocks: [],
-                 deactivated: false,
-                 default_scope: "public",
-                 domain_blocks: [],
-                 follower_count: 0,
-                 is_admin: false,
-                 is_moderator: false,
-                 keys: nil,
-                 locked: false,
-                 no_rich_text: false,
-                 note_count: 0,
-                 settings: nil,
-                 source_data: %{},
-                 hub: "https://social.heldscal.la/main/push/hub",
-                 magic_key:
-                   "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB",
-                 salmon: "https://social.heldscal.la/main/salmon/user/23211",
-                 topic: "https://social.heldscal.la/api/statuses/user_timeline/23211.atom",
-                 uri: "https://social.heldscal.la/user/23211"
-               }
-    end
-
-    test "find_make_or_update_actor takes an author element and returns an updated user" do
-      uri = "https://social.heldscal.la/user/23211"
-
-      {:ok, user} = OStatus.find_or_make_user(uri)
-      old_name = user.name
-      old_bio = user.bio
-      change = Ecto.Changeset.change(user, %{avatar: nil, bio: nil, name: nil})
-
-      {:ok, user} = Repo.update(change)
-      refute user.avatar
-
-      doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
-      [author] = :xmerl_xpath.string('//author[1]', doc)
-      {:ok, user} = OStatus.find_make_or_update_actor(author)
-      assert user.avatar["type"] == "Image"
-      assert user.name == old_name
-      assert user.bio == old_bio
-
-      {:ok, user_again} = OStatus.find_make_or_update_actor(author)
-      assert user_again == user
-    end
-
-    test "find_or_make_user disallows protocol downgrade" do
-      user = insert(:user, %{local: true})
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-
-      assert User.ap_enabled?(user)
-
-      user =
-        insert(:user, %{
-          ap_id: "https://social.heldscal.la/user/23211",
-          info: %{ap_enabled: true},
-          local: false
-        })
-
-      assert User.ap_enabled?(user)
-
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-      assert User.ap_enabled?(user)
-    end
-
-    test "find_make_or_update_actor disallows protocol downgrade" do
-      user = insert(:user, %{local: true})
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-
-      assert User.ap_enabled?(user)
-
-      user =
-        insert(:user, %{
-          ap_id: "https://social.heldscal.la/user/23211",
-          info: %{ap_enabled: true},
-          local: false
-        })
-
-      assert User.ap_enabled?(user)
-
-      {:ok, user} = OStatus.find_or_make_user(user.ap_id)
-      assert User.ap_enabled?(user)
-
-      doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
-      [author] = :xmerl_xpath.string('//author[1]', doc)
-      {:error, :invalid_protocol} = OStatus.find_make_or_update_actor(author)
-    end
-  end
-
-  describe "gathering user info from a user id" do
-    test "it returns user info in a hash" do
-      user = "shp@social.heldscal.la"
-
-      # TODO: make test local
-      {:ok, data} = OStatus.gather_user_info(user)
-
-      expected = %{
-        "hub" => "https://social.heldscal.la/main/push/hub",
-        "magic_key" =>
-          "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB",
-        "name" => "shp",
-        "nickname" => "shp",
-        "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
-        "subject" => "acct:shp@social.heldscal.la",
-        "topic" => "https://social.heldscal.la/api/statuses/user_timeline/29191.atom",
-        "uri" => "https://social.heldscal.la/user/29191",
-        "host" => "social.heldscal.la",
-        "fqn" => user,
-        "bio" => "cofe",
-        "avatar" => %{
-          "type" => "Image",
-          "url" => [
-            %{
-              "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg",
-              "mediaType" => "image/jpeg",
-              "type" => "Link"
-            }
-          ]
-        },
-        "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
-        "ap_id" => nil
-      }
-
-      assert data == expected
-    end
-
-    test "it works with the uri" do
-      user = "https://social.heldscal.la/user/29191"
-
-      # TODO: make test local
-      {:ok, data} = OStatus.gather_user_info(user)
-
-      expected = %{
-        "hub" => "https://social.heldscal.la/main/push/hub",
-        "magic_key" =>
-          "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB",
-        "name" => "shp",
-        "nickname" => "shp",
-        "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
-        "subject" => "https://social.heldscal.la/user/29191",
-        "topic" => "https://social.heldscal.la/api/statuses/user_timeline/29191.atom",
-        "uri" => "https://social.heldscal.la/user/29191",
-        "host" => "social.heldscal.la",
-        "fqn" => user,
-        "bio" => "cofe",
-        "avatar" => %{
-          "type" => "Image",
-          "url" => [
-            %{
-              "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg",
-              "mediaType" => "image/jpeg",
-              "type" => "Link"
-            }
-          ]
-        },
-        "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
-        "ap_id" => nil
-      }
-
-      assert data == expected
-    end
-  end
-
-  describe "fetching a status by it's HTML url" do
-    test "it builds a missing status from an html url" do
-      capture_log(fn ->
-        url = "https://shitposter.club/notice/2827873"
-        {:ok, [activity]} = OStatus.fetch_activity_from_url(url)
-
-        assert activity.data["actor"] == "https://shitposter.club/user/1"
-
-        assert activity.data["object"] ==
-                 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
-      end)
-    end
-
-    test "it works for atom notes, too" do
-      url = "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056"
-      {:ok, [activity]} = OStatus.fetch_activity_from_url(url)
-      assert activity.data["actor"] == "https://social.sakamoto.gq/users/eal"
-      assert activity.data["object"] == url
-    end
-  end
-
-  test "it doesn't add nil in the to field" do
-    incoming = File.read!("test/fixtures/nil_mention_entry.xml")
-    {:ok, [activity]} = OStatus.handle_incoming(incoming)
-
-    assert activity.data["to"] == [
-             "http://localhost:4001/users/atarifrosch@social.stopwatchingus-heidelberg.de/followers",
-             "https://www.w3.org/ns/activitystreams#Public"
-           ]
-  end
-
-  describe "is_representable?" do
-    test "Note objects are representable" do
-      note_activity = insert(:note_activity)
-
-      assert OStatus.is_representable?(note_activity)
-    end
-
-    test "Article objects are not representable" do
-      note_activity = insert(:note_activity)
-      note_object = Object.normalize(note_activity)
-
-      note_data =
-        note_object.data
-        |> Map.put("type", "Article")
-
-      Cachex.clear(:object_cache)
-
-      cs = Object.change(note_object, %{data: note_data})
-      {:ok, _article_object} = Repo.update(cs)
-
-      # the underlying object is now an Article instead of a note, so this should fail
-      refute OStatus.is_representable?(note_activity)
-    end
-  end
-
-  describe "make_user/2" do
-    test "creates new user" do
-      {:ok, user} = OStatus.make_user("https://social.heldscal.la/user/23211")
-
-      created_user =
-        User
-        |> Repo.get_by(ap_id: "https://social.heldscal.la/user/23211")
-        |> Map.put(:last_digest_emailed_at, nil)
-
-      assert user.info
-      assert user == created_user
-    end
-  end
-end
diff --git a/test/web/ostatus/user_representer_test.exs b/test/web/ostatus/user_representer_test.exs
deleted file mode 100644 (file)
index e3863d2..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.UserRepresenterTest do
-  use Pleroma.DataCase
-  alias Pleroma.Web.OStatus.UserRepresenter
-
-  import Pleroma.Factory
-  alias Pleroma.User
-
-  test "returns a user with id, uri, name and link" do
-    user = insert(:user, %{nickname: "レイン"})
-    tuple = UserRepresenter.to_simple_form(user)
-
-    res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string
-
-    expected = """
-    <id>#{user.ap_id}</id>
-    <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
-    <uri>#{user.ap_id}</uri>
-    <poco:preferredUsername>#{user.nickname}</poco:preferredUsername>
-    <poco:displayName>#{user.name}</poco:displayName>
-    <poco:note>#{user.bio}</poco:note>
-    <summary>#{user.bio}</summary>
-    <name>#{user.nickname}</name>
-    <link rel="avatar" href="#{User.avatar_url(user)}" />
-    <link rel="header" href="#{User.banner_url(user)}" />
-    <ap_enabled>true</ap_enabled>
-    """
-
-    assert clean(res) == clean(expected)
-  end
-
-  defp clean(string) do
-    String.replace(string, ~r/\s/, "")
-  end
-end
diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs
deleted file mode 100644 (file)
index 153ec41..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Salmon.SalmonTest do
-  use Pleroma.DataCase
-  alias Pleroma.Activity
-  alias Pleroma.Keys
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Web.Federator.Publisher
-  alias Pleroma.Web.Salmon
-  import Mock
-  import Pleroma.Factory
-
-  @magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
-
-  @wrong_magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAA"
-
-  @magickey_friendica "RSA.AMwa8FUs2fWEjX0xN7yRQgegQffhBpuKNC6fa5VNSVorFjGZhRrlPMn7TQOeihlc9lBz2OsHlIedbYn2uJ7yCs0.AQAB"
-
-  setup_all do
-    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "decodes a salmon" do
-    {:ok, salmon} = File.read("test/fixtures/salmon.xml")
-    {:ok, doc} = Salmon.decode_and_validate(@magickey, salmon)
-    assert Regex.match?(~r/xml/, doc)
-  end
-
-  test "errors on wrong magic key" do
-    {:ok, salmon} = File.read("test/fixtures/salmon.xml")
-    assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error
-  end
-
-  test "it encodes a magic key from a public key" do
-    key = Salmon.decode_key(@magickey)
-    magic_key = Salmon.encode_key(key)
-
-    assert @magickey == magic_key
-  end
-
-  test "it decodes a friendica public key" do
-    _key = Salmon.decode_key(@magickey_friendica)
-  end
-
-  test "encodes an xml payload with a private key" do
-    doc = File.read!("test/fixtures/incoming_note_activity.xml")
-    pem = File.read!("test/fixtures/private_key.pem")
-    {:ok, private, public} = Keys.keys_from_pem(pem)
-
-    # Let's try a roundtrip.
-    {:ok, salmon} = Salmon.encode(private, doc)
-    {:ok, decoded_doc} = Salmon.decode_and_validate(Salmon.encode_key(public), salmon)
-
-    assert doc == decoded_doc
-  end
-
-  test "it gets a magic key" do
-    salmon = File.read!("test/fixtures/salmon2.xml")
-    {:ok, key} = Salmon.fetch_magic_key(salmon)
-
-    assert key ==
-             "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB"
-  end
-
-  test_with_mock "it pushes an activity to remote accounts it's addressed to",
-                 Publisher,
-                 [:passthrough],
-                 [] do
-    user_data = %{
-      info: %{
-        salmon: "http://test-example.org/salmon"
-      },
-      local: false
-    }
-
-    mentioned_user = insert(:user, user_data)
-    note = insert(:note)
-
-    activity_data = %{
-      "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
-      "type" => "Create",
-      "actor" => note.data["actor"],
-      "to" => note.data["to"] ++ [mentioned_user.ap_id],
-      "object" => note.data,
-      "published_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
-      "context" => note.data["context"]
-    }
-
-    {:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
-    user = User.get_cached_by_ap_id(activity.data["actor"])
-    {:ok, user} = User.ensure_keys_present(user)
-
-    Salmon.publish(user, activity)
-
-    assert called(Publisher.enqueue_one(Salmon, %{recipient_id: mentioned_user.id}))
-  end
-end
index 696c1bd7067c7e9cd2b29deff185ac83e5fc8f18..5aa8c73cf8d01c8036eba217ad40ad6676cb3423 100644 (file)
@@ -45,19 +45,6 @@ defmodule Pleroma.Web.WebFingerTest do
       assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
     end
 
-    test "returns the info for an OStatus user" do
-      user = "shp@social.heldscal.la"
-
-      {:ok, data} = WebFinger.finger(user)
-
-      assert data["magic_key"] ==
-               "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB"
-
-      assert data["topic"] == "https://social.heldscal.la/api/statuses/user_timeline/29191.atom"
-      assert data["subject"] == "acct:shp@social.heldscal.la"
-      assert data["salmon"] == "https://social.heldscal.la/main/salmon/user/29191"
-    end
-
     test "returns the ActivityPub actor URI for an ActivityPub user" do
       user = "framasoft@framatube.org"
 
@@ -72,20 +59,6 @@ defmodule Pleroma.Web.WebFingerTest do
       assert data["ap_id"] == "https://gerzilla.de/channel/kaniini"
     end
 
-    test "returns the correctly for json ostatus users" do
-      user = "winterdienst@gnusocial.de"
-
-      {:ok, data} = WebFinger.finger(user)
-
-      assert data["magic_key"] ==
-               "RSA.qfYaxztz7ZELrE4v5WpJrPM99SKI3iv9Y3Tw6nfLGk-4CRljNYqV8IYX2FXjeucC_DKhPNnlF6fXyASpcSmA_qupX9WC66eVhFhZ5OuyBOeLvJ1C4x7Hi7Di8MNBxY3VdQuQR0tTaS_YAZCwASKp7H6XEid3EJpGt0EQZoNzRd8=.AQAB"
-
-      assert data["topic"] == "https://gnusocial.de/api/statuses/user_timeline/249296.atom"
-      assert data["subject"] == "acct:winterdienst@gnusocial.de"
-      assert data["salmon"] == "https://gnusocial.de/main/salmon/user/249296"
-      assert data["subscribe_address"] == "https://gnusocial.de/main/ostatussub?profile={uri}"
-    end
-
     test "it work for AP-only user" do
       user = "kpherox@mstdn.jp"
 
diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs
deleted file mode 100644 (file)
index f6d002b..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Websub.WebsubControllerTest do
-  use Pleroma.Web.ConnCase
-  import Pleroma.Factory
-  alias Pleroma.Repo
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-
-  clear_config_all([:instance, :federating]) do
-    Pleroma.Config.put([:instance, :federating], true)
-  end
-
-  test "websub subscription request", %{conn: conn} do
-    user = insert(:user)
-
-    path = Pleroma.Web.OStatus.pubsub_path(user)
-
-    data = %{
-      "hub.callback": "http://example.org/sub",
-      "hub.mode": "subscribe",
-      "hub.topic": Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret": "a random secret",
-      "hub.lease_seconds": "100"
-    }
-
-    conn =
-      conn
-      |> post(path, data)
-
-    assert response(conn, 202) == "Accepted"
-  end
-
-  test "websub subscription confirmation", %{conn: conn} do
-    websub = insert(:websub_client_subscription)
-
-    params = %{
-      "hub.mode" => "subscribe",
-      "hub.topic" => websub.topic,
-      "hub.challenge" => "some challenge",
-      "hub.lease_seconds" => "100"
-    }
-
-    conn =
-      conn
-      |> get("/push/subscriptions/#{websub.id}", params)
-
-    websub = Repo.get(WebsubClientSubscription, websub.id)
-
-    assert response(conn, 200) == "some challenge"
-    assert websub.state == "accepted"
-    assert_in_delta NaiveDateTime.diff(websub.valid_until, NaiveDateTime.utc_now()), 100, 5
-  end
-
-  describe "websub_incoming" do
-    test "accepts incoming feed updates", %{conn: conn} do
-      websub = insert(:websub_client_subscription)
-      doc = "some stuff"
-      signature = Websub.sign(websub.secret, doc)
-
-      conn =
-        conn
-        |> put_req_header("x-hub-signature", "sha1=" <> signature)
-        |> put_req_header("content-type", "application/atom+xml")
-        |> post("/push/subscriptions/#{websub.id}", doc)
-
-      assert response(conn, 200) == "OK"
-    end
-
-    test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
-      websub = insert(:websub_client_subscription)
-      doc = "some stuff"
-      signature = Websub.sign("wrong secret", doc)
-
-      conn =
-        conn
-        |> put_req_header("x-hub-signature", "sha1=" <> signature)
-        |> put_req_header("content-type", "application/atom+xml")
-        |> post("/push/subscriptions/#{websub.id}", doc)
-
-      assert response(conn, 500) == "Error"
-    end
-  end
-end
diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs
deleted file mode 100644 (file)
index 46ca545..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.WebsubTest do
-  use Pleroma.DataCase
-  use Oban.Testing, repo: Pleroma.Repo
-
-  alias Pleroma.Tests.ObanHelpers
-  alias Pleroma.Web.Router.Helpers
-  alias Pleroma.Web.Websub
-  alias Pleroma.Web.Websub.WebsubClientSubscription
-  alias Pleroma.Web.Websub.WebsubServerSubscription
-  alias Pleroma.Workers.SubscriberWorker
-
-  import Pleroma.Factory
-  import Tesla.Mock
-
-  setup do
-    mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-    :ok
-  end
-
-  test "a verification of a request that is accepted" do
-    sub = insert(:websub_subscription)
-    topic = sub.topic
-
-    getter = fn _path, _headers, options ->
-      %{
-        "hub.challenge": challenge,
-        "hub.lease_seconds": seconds,
-        "hub.topic": ^topic,
-        "hub.mode": "subscribe"
-      } = Keyword.get(options, :params)
-
-      assert String.to_integer(seconds) > 0
-
-      {:ok,
-       %Tesla.Env{
-         status: 200,
-         body: challenge
-       }}
-    end
-
-    {:ok, sub} = Websub.verify(sub, getter)
-    assert sub.state == "active"
-  end
-
-  test "a verification of a request that doesn't return 200" do
-    sub = insert(:websub_subscription)
-
-    getter = fn _path, _headers, _options ->
-      {:ok,
-       %Tesla.Env{
-         status: 500,
-         body: ""
-       }}
-    end
-
-    {:error, sub} = Websub.verify(sub, getter)
-    # Keep the current state.
-    assert sub.state == "requested"
-  end
-
-  test "an incoming subscription request" do
-    user = insert(:user)
-
-    data = %{
-      "hub.callback" => "http://example.org/sub",
-      "hub.mode" => "subscribe",
-      "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret" => "a random secret",
-      "hub.lease_seconds" => "100"
-    }
-
-    {:ok, subscription} = Websub.incoming_subscription_request(user, data)
-    assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
-    assert subscription.state == "requested"
-    assert subscription.secret == "a random secret"
-    assert subscription.callback == "http://example.org/sub"
-  end
-
-  test "an incoming subscription request for an existing subscription" do
-    user = insert(:user)
-
-    sub =
-      insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user))
-
-    data = %{
-      "hub.callback" => sub.callback,
-      "hub.mode" => "subscribe",
-      "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
-      "hub.secret" => "a random secret",
-      "hub.lease_seconds" => "100"
-    }
-
-    {:ok, subscription} = Websub.incoming_subscription_request(user, data)
-    assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
-    assert subscription.state == sub.state
-    assert subscription.secret == "a random secret"
-    assert subscription.callback == sub.callback
-    assert length(Repo.all(WebsubServerSubscription)) == 1
-    assert subscription.id == sub.id
-  end
-
-  def accepting_verifier(subscription) do
-    {:ok, %{subscription | state: "accepted"}}
-  end
-
-  test "initiate a subscription for a given user and topic" do
-    subscriber = insert(:user)
-    user = insert(:user, %{info: %Pleroma.User.Info{topic: "some_topic", hub: "some_hub"}})
-
-    {:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1)
-    assert websub.subscribers == [subscriber.ap_id]
-    assert websub.topic == "some_topic"
-    assert websub.hub == "some_hub"
-    assert is_binary(websub.secret)
-    assert websub.user == user
-    assert websub.state == "accepted"
-  end
-
-  test "discovers the hub and canonical url" do
-    topic = "https://mastodon.social/users/lambadalambda.atom"
-
-    {:ok, discovered} = Websub.gather_feed_data(topic)
-
-    expected = %{
-      "hub" => "https://mastodon.social/api/push",
-      "uri" => "https://mastodon.social/users/lambadalambda",
-      "nickname" => "lambadalambda",
-      "name" => "Critical Value",
-      "host" => "mastodon.social",
-      "bio" => "a cool dude.",
-      "avatar" => %{
-        "type" => "Image",
-        "url" => [
-          %{
-            "href" =>
-              "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244",
-            "mediaType" => "image/gif",
-            "type" => "Link"
-          }
-        ]
-      }
-    }
-
-    assert expected == discovered
-  end
-
-  test "calls the hub, requests topic" do
-    hub = "https://social.heldscal.la/main/push/hub"
-    topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, data}, _headers ->
-      assert Keyword.get(data, :"hub.mode") == "subscribe"
-
-      assert Keyword.get(data, :"hub.callback") ==
-               Helpers.websub_url(
-                 Pleroma.Web.Endpoint,
-                 :websub_subscription_confirmation,
-                 websub.id
-               )
-
-      {:ok, %{status: 202}}
-    end
-
-    task = Task.async(fn -> Websub.request_subscription(websub, poster) end)
-
-    change = Ecto.Changeset.change(websub, %{state: "accepted"})
-    {:ok, _} = Repo.update(change)
-
-    {:ok, websub} = Task.await(task)
-
-    assert websub.state == "accepted"
-  end
-
-  test "rejects the subscription if it can't be accepted" do
-    hub = "https://social.heldscal.la/main/push/hub"
-    topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, _data}, _headers ->
-      {:ok, %{status: 202}}
-    end
-
-    {:error, websub} = Websub.request_subscription(websub, poster, 1000)
-    assert websub.state == "rejected"
-
-    websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
-
-    poster = fn ^hub, {:form, _data}, _headers ->
-      {:ok, %{status: 400}}
-    end
-
-    {:error, websub} = Websub.request_subscription(websub, poster, 1000)
-    assert websub.state == "rejected"
-  end
-
-  test "sign a text" do
-    signed = Websub.sign("secret", "text")
-    assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase()
-
-    _signed = Websub.sign("secret", [["て"], ['す']])
-  end
-
-  describe "renewing subscriptions" do
-    test "it renews subscriptions that have less than a day of time left" do
-      day = 60 * 60 * 24
-      now = NaiveDateTime.utc_now()
-
-      still_good =
-        insert(:websub_client_subscription, %{
-          valid_until: NaiveDateTime.add(now, 2 * day),
-          topic: "http://example.org/still_good",
-          hub: "http://example.org/still_good",
-          state: "accepted"
-        })
-
-      needs_refresh =
-        insert(:websub_client_subscription, %{
-          valid_until: NaiveDateTime.add(now, day - 100),
-          topic: "http://example.org/needs_refresh",
-          hub: "http://example.org/needs_refresh",
-          state: "accepted"
-        })
-
-      _refresh = Websub.refresh_subscriptions()
-      ObanHelpers.perform(all_enqueued(worker: SubscriberWorker))
-
-      assert still_good == Repo.get(WebsubClientSubscription, still_good.id)
-      refute needs_refresh == Repo.get(WebsubClientSubscription, needs_refresh.id)
-    end
-  end
-end