Merge branch 'stable' into stable-sync/2.1.1
authorrinpatch <rinpatch@sdf.org>
Tue, 8 Sep 2020 17:34:02 +0000 (20:34 +0300)
committerrinpatch <rinpatch@sdf.org>
Tue, 8 Sep 2020 17:34:02 +0000 (20:34 +0300)
74 files changed:
CC-BY-4.0 [new file with mode: 0644]
CHANGELOG.md
Dockerfile
SECURITY.md
config/config.exs
config/description.exs
config/test.exs
docs/administration/updating.md
docs/configuration/cheatsheet.md
lib/mix/pleroma.ex
lib/mix/tasks/pleroma/benchmark.ex
lib/mix/tasks/pleroma/frontend.ex
lib/pleroma/application.ex
lib/pleroma/config/deprecation_warnings.ex
lib/pleroma/config/oban.ex [new file with mode: 0644]
lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex [new file with mode: 0644]
lib/pleroma/gun/conn.ex
lib/pleroma/http/adapter_helper.ex
lib/pleroma/http/adapter_helper/gun.ex
lib/pleroma/http/adapter_helper/hackney.ex
lib/pleroma/http/ex_aws.ex
lib/pleroma/http/http.ex
lib/pleroma/http/tzdata.ex
lib/pleroma/notification.ex
lib/pleroma/object/fetcher.ex
lib/pleroma/stats.ex
lib/pleroma/tesla/middleware/connection_pool.ex [new file with mode: 0644]
lib/pleroma/tesla/middleware/follow_redirects.ex [deleted file]
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
lib/pleroma/web/activity_pub/object_validators/audio_validator.ex
lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
lib/pleroma/web/activity_pub/object_validators/event_validator.ex
lib/pleroma/web/activity_pub/object_validators/note_validator.ex
lib/pleroma/web/activity_pub/object_validators/question_validator.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/api_spec/operations/list_operation.ex
lib/pleroma/web/auth/pleroma_authenticator.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/controllers/list_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/metadata/opengraph.ex
lib/pleroma/web/metadata/twitter_card.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
lib/pleroma/web/rel_me.ex
lib/pleroma/web/rich_media/helpers.ex
lib/pleroma/web/rich_media/parser.ex
lib/pleroma/workers/cron/stats_worker.ex [deleted file]
mix.exs
mix.lock
priv/repo/migrations/20200831142509_chat_constraints.exs [new file with mode: 0644]
priv/repo/migrations/20200901061256_ensure_bio_is_string.exs [new file with mode: 0644]
priv/repo/migrations/20200901061637_bio_set_not_null.exs [new file with mode: 0644]
priv/repo/migrations/20200905082737_rename_await_up_timeout_in_connections_pool.exs [new file with mode: 0644]
priv/repo/migrations/20200905091427_rename_timeout_in_pools.exs [new file with mode: 0644]
priv/repo/migrations/20200906072147_remove_cron_stats_worker_from_oban_config.exs [new file with mode: 0644]
test/chat_test.exs
test/config/deprecation_warnings_test.exs
test/stats_test.exs
test/tasks/frontend_test.exs
test/user_test.exs
test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs [new file with mode: 0644]
test/web/activity_pub/object_validators/chat_validation_test.exs
test/web/activity_pub/transmogrifier/question_handling_test.exs
test/web/admin_api/controllers/admin_api_controller_test.exs
test/web/common_api/common_api_test.exs
test/web/instances/instance_test.exs
test/web/mastodon_api/controllers/list_controller_test.exs
test/web/mastodon_api/views/account_view_test.exs
test/web/rich_media/parser_test.exs
test/web/twitter_api/util_controller_test.exs

diff --git a/CC-BY-4.0 b/CC-BY-4.0
new file mode 100644 (file)
index 0000000..4ea99c2
--- /dev/null
+++ b/CC-BY-4.0
@@ -0,0 +1,395 @@
+Attribution 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+    wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More considerations
+     for the public:
+    wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution 4.0 International Public License ("Public License"). To the
+extent this Public License may be interpreted as a contract, You are
+granted the Licensed Rights in consideration of Your acceptance of
+these terms and conditions, and the Licensor grants You such rights in
+consideration of benefits the Licensor receives from making the
+Licensed Material available under these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  d. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  e. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  f. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  g. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  h. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  i. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  j. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  k. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part; and
+
+            b. produce, reproduce, and Share Adapted Material.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+       4. If You Share Adapted Material You produce, the Adapter's
+          License You apply must not prevent recipients of the Adapted
+          Material from complying with this Public License.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material; and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
index 92635f6d0984a5792b85bd103788ca1117c9ade6..19b2596cc32aa640263a8fe9cef104585f2714ac 100644 (file)
@@ -3,6 +3,17 @@ 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
+
+### Changed
+
+- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
+- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
+
+### Removed
+
+- **Breaking:** Removed `Pleroma.Workers.Cron.StatsWorker` setting from Oban `:crontab`.
+
 ## [2.1.1] - 2020-09-08
 
 ### Security
index aa50e27ecf9607718704b38637b50a537e89ba98..c210cf79c777629bdb32425540626adcc4faa8fb 100644 (file)
@@ -31,7 +31,7 @@ LABEL maintainer="ops@pleroma.social" \
 ARG HOME=/opt/pleroma
 ARG DATA=/var/lib/pleroma
 
-RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
+RUN echo "https://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
        apk update &&\
        apk add exiftool imagemagick ncurses postgresql-client &&\
        adduser --system --shell /bin/false --home ${HOME} pleroma &&\
index c212a250581ccf5e5c30b81449fd58b3e47a592a..8617c1434644501de94047b155a228ae3a40198b 100644 (file)
@@ -6,7 +6,7 @@ Currently, Pleroma offers bugfixes and security patches only for the latest mino
 
 | Version | Support 
 |---------| --------
-| 2.0     | Bugfixes and security patches
+| 2.1     | Bugfixes and security patches
 
 ## Reporting a vulnerability
 
index 88f6125e5bba3299d96313341d8b552859377838..1a2b312b500e5b236620a9e24771e49cf0be79ef 100644 (file)
@@ -544,7 +544,6 @@ config :pleroma, Oban,
   plugins: [Oban.Plugins.Pruner],
   crontab: [
     {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
-    {"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
     {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
     {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
     {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
@@ -670,7 +669,7 @@ config :pleroma, :static_fe, enabled: false
 # With no frontend configuration, the bundled files from the `static` directory will
 # be used.
 #
-# config :pleroma, :frontends, 
+# config :pleroma, :frontends,
 # primary: %{"name" => "pleroma-fe", "ref" => "develop"},
 # admin: %{"name" => "admin-fe", "ref" => "stable"},
 # available: %{...}
@@ -734,28 +733,28 @@ config :pleroma, :connections_pool,
   max_connections: 250,
   max_idle_time: 30_000,
   retry: 0,
-  await_up_timeout: 5_000
+  connect_timeout: 5_000
 
 config :pleroma, :pools,
   federation: [
     size: 50,
     max_waiting: 10,
-    timeout: 10_000
+    recv_timeout: 10_000
   ],
   media: [
     size: 50,
     max_waiting: 10,
-    timeout: 10_000
+    recv_timeout: 10_000
   ],
   upload: [
     size: 25,
     max_waiting: 5,
-    timeout: 15_000
+    recv_timeout: 15_000
   ],
   default: [
     size: 10,
     max_waiting: 2,
-    timeout: 5_000
+    recv_timeout: 5_000
   ]
 
 config :pleroma, :hackney_pools,
index 5e08ba109d98b898d9a63c35e382a8d4ff68aca1..eac97ad64c5a864d93bce5e40db65cc27b73c01e 100644 (file)
@@ -2291,7 +2291,6 @@ config :pleroma, :config_description, [
         description: "Settings for cron background jobs",
         suggestions: [
           {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
-          {"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
           {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
           {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
           {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
@@ -3378,7 +3377,7 @@ config :pleroma, :config_description, [
         suggestions: [250]
       },
       %{
-        key: :await_up_timeout,
+        key: :connect_timeout,
         type: :integer,
         description: "Timeout while `gun` will wait until connection is up. Default: 5000ms.",
         suggestions: [5000]
@@ -3416,6 +3415,12 @@ config :pleroma, :config_description, [
               description:
                 "Maximum number of requests waiting for other requests to finish. After this number is reached, the pool will start returning errrors when a new request is made",
               suggestions: [10]
+            },
+            %{
+              key: :recv_timeout,
+              type: :integer,
+              description: "Timeout for the pool while gun will wait for response",
+              suggestions: [10_000]
             }
           ]
         }
index e9c2273e8560756e4c0b7ada65743ca2a9e73c9b..0ee6f1b7f3043fd543f375612d9792353c2cccb8 100644 (file)
@@ -114,8 +114,6 @@ config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false
 
 config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
 
-config :pleroma, :instances_favicons, enabled: false
-
 config :pleroma, Pleroma.Uploaders.S3,
   bucket: nil,
   streaming_enabled: true,
index c994f3f16c0ddd5ed7e360125737b47f582ebe99..ef2c9218c339827aa736f1aaba4fffdea72a9fe8 100644 (file)
@@ -18,9 +18,10 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
 
 1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
 2. Run `git pull`. This pulls the latest changes from upstream.
-3. Run `mix deps.get`. This pulls in any new dependencies.
+3. Run `mix deps.get` [^1]. This pulls in any new dependencies.
 4. Stop the Pleroma service.
-5. Run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
+5. Run `mix ecto.migrate` [^1] [^2]. This task performs database migrations, if there were any.
 6. Start the Pleroma service.
 
-[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
+[^1]: Depending on which install guide you followed (for example on Debian/Ubuntu), you want to run `mix` tasks as `pleroma` user by adding `sudo -Hu pleroma` before the command.
+[^2]: Prefix with `MIX_ENV=prod` to run it using the production config file.
index b2980793d8693691fd730baebf9c7c339b28936c..ec59896ecd16dba049606ae13d18c3ade799fd4f 100644 (file)
@@ -115,6 +115,7 @@ To add configuration to your config file, you can copy it from the base config.
     * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
     * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
     * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.ActivityExpiration` to be enabled for processing the scheduled delections.
+    * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
 * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
 * `transparency_exclusions`: Exclude specific instance names from MRF transparency.  The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
 
@@ -496,7 +497,7 @@ Settings for HTTP connection pool.
 * `:connection_acquisition_wait` - Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries.
 * `connection_acquisition_retries` - Number of attempts to acquire the connection from the pool if it is overloaded. Each attempt is timed `:connection_acquisition_wait` apart.
 * `:max_connections` - Maximum number of connections in the pool.
-* `:await_up_timeout` - Timeout to connect to the host.
+* `:connect_timeout` - Timeout to connect to the host.
 * `:reclaim_multiplier` - Multiplied by `:max_connections` this will be the maximum number of idle connections that will be reclaimed in case the pool is overloaded.
 
 ### :pools
@@ -515,7 +516,7 @@ There are four pools used:
 For each pool, the options are:
 
 * `:size` - limit to how much requests can be concurrently executed.
-* `:timeout` - timeout while `gun` will wait for response
+* `:recv_timeout` - timeout while `gun` will wait for response
 * `:max_waiting` - limit to how much requests can be waiting for others to finish, after this is reached, subsequent requests will be dropped.
 
 ## Captcha
index fe9b0d16c3b4f413303d7cbceb2d9cc212c7337b..49ba2aae4b0d6daf9f6bc3356015e6cc062ab976 100644 (file)
@@ -18,6 +18,7 @@ defmodule Mix.Pleroma do
   @doc "Common functions to be reused in mix tasks"
   def start_pleroma do
     Pleroma.Config.Holder.save_default()
+    Pleroma.Config.Oban.warn()
     Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
 
     if Pleroma.Config.get(:env) != :test do
index dd2b9c8f278b26d28506b14a413d341a704c6417..a607d5d4ff76b0b883cef63ab395f58210081a40 100644 (file)
@@ -91,20 +91,17 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
         "Without conn and without pool" => fn ->
           {:ok, %Tesla.Env{}} =
             Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
-              adapter: [pool: :no_pool, receive_conn: false]
+              pool: :no_pool,
+              receive_conn: false
             )
         end,
         "Without conn and with pool" => fn ->
           {:ok, %Tesla.Env{}} =
-            Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
-              adapter: [receive_conn: false]
-            )
+            Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], receive_conn: false)
         end,
         "With reused conn and without pool" => fn ->
           {:ok, %Tesla.Env{}} =
-            Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
-              adapter: [pool: :no_pool]
-            )
+            Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], pool: :no_pool)
         end,
         "With reused conn and with pool" => fn ->
           {:ok, %Tesla.Env{}} = Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500")
index 484af6da70bd3d77ba6aa3c91b0499e6490aa3d4..cbce81ab9666919d0f163a36c612d0482892f98a 100644 (file)
@@ -69,7 +69,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
 
     fe_label = "#{frontend} (#{ref})"
 
-    tmp_dir = Path.join(dest, "tmp")
+    tmp_dir = Path.join([instance_static_dir, "frontends", "tmp"])
 
     with {_, :ok} <-
            {:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, options[:file])},
@@ -124,9 +124,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
     url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
 
     with {:ok, %{status: 200, body: zip_body}} <-
-           Pleroma.HTTP.get(url, [],
-             adapter: [pool: :media, timeout: 120_000, recv_timeout: 120_000]
-           ) do
+           Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
       unzip(zip_body, dest)
     else
       e -> {:error, e}
@@ -135,6 +133,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
 
   defp install_frontend(frontend_info, source, dest) do
     from = frontend_info["build_dir"] || "dist"
+    File.rm_rf!(dest)
     File.mkdir_p!(dest)
     File.cp_r!(Path.join([source, from]), dest)
     :ok
index c0b5db9f16affbe235595194f114a5b315ceb443..c39e24919c4c76e4ce0c682f99767e71106ba234 100644 (file)
@@ -22,13 +22,18 @@ defmodule Pleroma.Application do
   def repository, do: @repository
 
   def user_agent do
-    case Config.get([:http, :user_agent], :default) do
-      :default ->
-        info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
-        named_version() <> "; " <> info
-
-      custom ->
-        custom
+    if Process.whereis(Pleroma.Web.Endpoint) do
+      case Config.get([:http, :user_agent], :default) do
+        :default ->
+          info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
+          named_version() <> "; " <> info
+
+        custom ->
+          custom
+      end
+    else
+      # fallback, if endpoint is not started yet
+      "Pleroma Data Loader"
     end
   end
 
@@ -39,9 +44,13 @@ defmodule Pleroma.Application do
     # every time the application is restarted, so we disable module
     # conflicts at runtime
     Code.compiler_options(ignore_module_conflict: true)
+    # Disable warnings_as_errors at runtime, it breaks Phoenix live reload
+    # due to protocol consolidation warnings
+    Code.compiler_options(warnings_as_errors: false)
     Pleroma.Telemetry.Logger.attach()
     Config.Holder.save_default()
     Pleroma.HTML.compile_scrubbers()
+    Pleroma.Config.Oban.warn()
     Config.DeprecationWarnings.warn()
     Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
     Pleroma.ApplicationRequirements.verify!()
index 0f52eb210da75ca3196489e1c5814026de7aa7fa..2bfe4ddbacdc1c69a87e30ff6f520c4c0769d290 100644 (file)
@@ -56,6 +56,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
     check_old_mrf_config()
     check_media_proxy_whitelist_config()
     check_welcome_message_config()
+    check_gun_pool_options()
   end
 
   def check_welcome_message_config do
@@ -115,4 +116,46 @@ defmodule Pleroma.Config.DeprecationWarnings do
       """)
     end
   end
+
+  def check_gun_pool_options do
+    pool_config = Config.get(:connections_pool)
+
+    if timeout = pool_config[:await_up_timeout] do
+      Logger.warn("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using old setting name `await_up_timeout` instead of `connect_timeout`. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
+      """)
+
+      Config.put(:connections_pool, Keyword.put_new(pool_config, :connect_timeout, timeout))
+    end
+
+    pools_configs = Config.get(:pools)
+
+    warning_preface = """
+    !!!DEPRECATION WARNING!!!
+    Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
+    """
+
+    updated_config =
+      Enum.reduce(pools_configs, [], fn {pool_name, config}, acc ->
+        if timeout = config[:timeout] do
+          Keyword.put(acc, pool_name, Keyword.put_new(config, :recv_timeout, timeout))
+        else
+          acc
+        end
+      end)
+
+    if updated_config != [] do
+      pool_warnings =
+        updated_config
+        |> Keyword.keys()
+        |> Enum.map(fn pool_name ->
+          "\n* `:timeout` options in #{pool_name} pool is now `:recv_timeout`"
+        end)
+
+      Logger.warn(Enum.join([warning_preface | pool_warnings]))
+
+      Config.put(:pools, updated_config)
+    end
+  end
 end
diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex
new file mode 100644 (file)
index 0000000..c2d56eb
--- /dev/null
@@ -0,0 +1,30 @@
+defmodule Pleroma.Config.Oban do
+  require Logger
+
+  def warn do
+    oban_config = Pleroma.Config.get(Oban)
+
+    crontab =
+      [Pleroma.Workers.Cron.StatsWorker]
+      |> Enum.reduce(oban_config[:crontab], fn removed_worker, acc ->
+        with acc when is_list(acc) <- acc,
+             setting when is_tuple(setting) <-
+               Enum.find(acc, fn {_, worker} -> worker == removed_worker end) do
+          """
+          !!!OBAN CONFIG WARNING!!!
+          You are using old workers in Oban crontab settings, which were removed.
+          Please, remove setting from crontab in your config file (prod.secret.exs): #{
+            inspect(setting)
+          }
+          """
+          |> Logger.warn()
+
+          List.delete(acc, setting)
+        else
+          _ -> acc
+        end
+      end)
+
+    Pleroma.Config.put(Oban, Keyword.put(oban_config, :crontab, crontab))
+  end
+end
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex
new file mode 100644 (file)
index 0000000..4aacc5c
--- /dev/null
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do
+  use Ecto.Type
+
+  def type, do: :map
+
+  def cast(data) when is_map(data) do
+    has_invalid_emoji? =
+      Enum.find(data, fn
+        {name, uri} when is_binary(name) and is_binary(uri) ->
+          # based on ObjectValidators.Uri.cast()
+          case URI.parse(uri) do
+            %URI{host: nil} -> true
+            %URI{host: ""} -> true
+            %URI{scheme: scheme} when scheme in ["https", "http"] -> false
+            _ -> true
+          end
+
+        {_name, _uri} ->
+          true
+      end)
+
+    if has_invalid_emoji?, do: :error, else: {:ok, data}
+  end
+
+  def cast(_data), do: :error
+
+  def dump(data), do: {:ok, data}
+
+  def load(data), do: {:ok, data}
+end
index a3f75a4bbfb3248e361389ae6419318b9abfa799..75b1ffc0a6a1cef762cc44de0815047eb5efb0d3 100644 (file)
@@ -13,7 +13,7 @@ defmodule Pleroma.Gun.Conn do
     opts =
       opts
       |> Enum.into(%{})
-      |> Map.put_new(:await_up_timeout, pool_opts[:await_up_timeout] || 5_000)
+      |> Map.put_new(:connect_timeout, pool_opts[:connect_timeout] || 5_000)
       |> Map.put_new(:supervise, false)
       |> maybe_add_tls_opts(uri)
 
@@ -50,7 +50,7 @@ defmodule Pleroma.Gun.Conn do
 
     with open_opts <- Map.delete(opts, :tls_opts),
          {:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
-         {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]),
+         {:ok, _} <- Gun.await_up(conn, opts[:connect_timeout]),
          stream <- Gun.connect(conn, connect_opts),
          {:response, :fin, 200, _} <- Gun.await(conn, stream) do
       {:ok, conn}
@@ -88,7 +88,7 @@ defmodule Pleroma.Gun.Conn do
       |> Map.put(:socks_opts, socks_opts)
 
     with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts),
-         {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
+         {:ok, _} <- Gun.await_up(conn, opts[:connect_timeout]) do
       {:ok, conn}
     else
       error ->
@@ -106,7 +106,7 @@ defmodule Pleroma.Gun.Conn do
     host = Pleroma.HTTP.AdapterHelper.parse_host(host)
 
     with {:ok, conn} <- Gun.open(host, port, opts),
-         {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
+         {:ok, _} <- Gun.await_up(conn, opts[:connect_timeout]) do
       {:ok, conn}
     else
       error ->
index 0728cbaa2c69a42182d5a048ac0028caaf285575..08b51578a31066c3ed296a4c27fc48bfc566edf0 100644 (file)
@@ -6,7 +6,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
   @moduledoc """
   Configure Tesla.Client with default and customized adapter options.
   """
-  @defaults [pool: :federation]
+  @defaults [pool: :federation, connect_timeout: 5_000, recv_timeout: 5_000]
 
   @type proxy_type() :: :socks4 | :socks5
   @type host() :: charlist() | :inet.ip_address()
@@ -19,7 +19,6 @@ defmodule Pleroma.HTTP.AdapterHelper do
           | {Connection.proxy_type(), Connection.host(), pos_integer()}
 
   @callback options(keyword(), URI.t()) :: keyword()
-  @callback get_conn(URI.t(), keyword()) :: {:ok, term()} | {:error, term()}
 
   @spec format_proxy(String.t() | tuple() | nil) :: proxy() | nil
   def format_proxy(nil), do: nil
@@ -47,9 +46,6 @@ defmodule Pleroma.HTTP.AdapterHelper do
     |> adapter_helper().options(uri)
   end
 
-  @spec get_conn(URI.t(), keyword()) :: {:ok, keyword()} | {:error, atom()}
-  def get_conn(uri, opts), do: adapter_helper().get_conn(uri, opts)
-
   defp adapter, do: Application.get_env(:tesla, :adapter)
 
   defp adapter_helper do
index 02e20f2d1aa408860ffd8239a70516957464d160..1dbb71362e934d6607b105a6f5cb73dd19100e5d 100644 (file)
@@ -6,18 +6,13 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
   @behaviour Pleroma.HTTP.AdapterHelper
 
   alias Pleroma.Config
-  alias Pleroma.Gun.ConnectionPool
   alias Pleroma.HTTP.AdapterHelper
 
   require Logger
 
   @defaults [
-    connect_timeout: 5_000,
-    domain_lookup_timeout: 5_000,
-    tls_handshake_timeout: 5_000,
     retry: 1,
-    retry_timeout: 1000,
-    await_up_timeout: 5_000
+    retry_timeout: 1_000
   ]
 
   @type pool() :: :federation | :upload | :media | :default
@@ -46,23 +41,17 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
   end
 
   defp put_timeout(opts) do
+    {recv_timeout, opts} = Keyword.pop(opts, :recv_timeout, pool_timeout(opts[:pool]))
     # this is the timeout to receive a message from Gun
-    Keyword.put_new(opts, :timeout, pool_timeout(opts[:pool]))
+    # `:timeout` key is used in Tesla
+    Keyword.put(opts, :timeout, recv_timeout)
   end
 
   @spec pool_timeout(pool()) :: non_neg_integer()
   def pool_timeout(pool) do
-    default = Config.get([:pools, :default, :timeout], 5_000)
+    default = Config.get([:pools, :default, :recv_timeout], 5_000)
 
-    Config.get([:pools, pool, :timeout], default)
-  end
-
-  @spec get_conn(URI.t(), keyword()) :: {:ok, keyword()} | {:error, atom()}
-  def get_conn(uri, opts) do
-    case ConnectionPool.get_conn(uri, opts) do
-      {:ok, conn_pid} -> {:ok, Keyword.merge(opts, conn: conn_pid, close_conn: false)}
-      err -> err
-    end
+    Config.get([:pools, pool, :recv_timeout], default)
   end
 
   @prefix Pleroma.Gun.ConnectionPool
index 62bd42485ab9443dbc3798b241b32fc3d16de94f..ef84553c1c50efee2dc04e7cb64d35821fb87b72 100644 (file)
@@ -2,11 +2,8 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
   @behaviour Pleroma.HTTP.AdapterHelper
 
   @defaults [
-    connect_timeout: 10_000,
-    recv_timeout: 20_000,
     follow_redirect: true,
-    force_redirect: true,
-    pool: :federation
+    force_redirect: true
   ]
 
   @spec options(keyword(), URI.t()) :: keyword()
@@ -19,6 +16,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
     |> Keyword.merge(config_opts)
     |> Keyword.merge(connection_opts)
     |> add_scheme_opts(uri)
+    |> maybe_add_with_body()
     |> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
   end
 
@@ -28,6 +26,11 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
 
   defp add_scheme_opts(opts, _), do: opts
 
-  @spec get_conn(URI.t(), keyword()) :: {:ok, keyword()}
-  def get_conn(_uri, opts), do: {:ok, opts}
+  defp maybe_add_with_body(opts) do
+    if opts[:max_body] do
+      Keyword.put(opts, :with_body, true)
+    else
+      opts
+    end
+  end
 end
index c3f335c7326cc62471d08773ba70e894f6c81465..5cac3532f7cfd899b64b1c038106a1d2ce36ab5f 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.HTTP.ExAws do
 
   @impl true
   def request(method, url, body \\ "", headers \\ [], http_opts \\ []) do
-    http_opts = Keyword.put_new(http_opts, :adapter, pool: :upload)
+    http_opts = Keyword.put_new(http_opts, :pool, :upload)
 
     case HTTP.request(method, url, body, headers, http_opts) do
       {:ok, env} ->
index b37b3fa8927c252a9b357e51d5635ca20f5d2376..052597191fcc90d48641217f6002418462893031 100644 (file)
@@ -60,30 +60,23 @@ defmodule Pleroma.HTTP do
           {:ok, Env.t()} | {:error, any()}
   def request(method, url, body, headers, options) when is_binary(url) do
     uri = URI.parse(url)
-    adapter_opts = AdapterHelper.options(uri, options[:adapter] || [])
-
-    case AdapterHelper.get_conn(uri, adapter_opts) do
-      {:ok, adapter_opts} ->
-        options = put_in(options[:adapter], adapter_opts)
-        params = options[:params] || []
-        request = build_request(method, headers, options, url, body, params)
-
-        adapter = Application.get_env(:tesla, :adapter)
-
-        client = Tesla.client(adapter_middlewares(adapter), adapter)
-
-        maybe_limit(
-          fn ->
-            request(client, request)
-          end,
-          adapter,
-          adapter_opts
-        )
-
-      # Connection release is handled in a custom FollowRedirects middleware
-      err ->
-        err
-    end
+    adapter_opts = AdapterHelper.options(uri, options || [])
+
+    options = put_in(options[:adapter], adapter_opts)
+    params = options[:params] || []
+    request = build_request(method, headers, options, url, body, params)
+
+    adapter = Application.get_env(:tesla, :adapter)
+
+    client = Tesla.client(adapter_middlewares(adapter), adapter)
+
+    maybe_limit(
+      fn ->
+        request(client, request)
+      end,
+      adapter,
+      adapter_opts
+    )
   end
 
   @spec request(Client.t(), keyword()) :: {:ok, Env.t()} | {:error, any()}
@@ -110,7 +103,7 @@ defmodule Pleroma.HTTP do
   end
 
   defp adapter_middlewares(Tesla.Adapter.Gun) do
-    [Pleroma.HTTP.Middleware.FollowRedirects]
+    [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool]
   end
 
   defp adapter_middlewares(_), do: []
index 4539ac3599aa02fe15090b3c416e5990617474ea..09cfdadf7c8ca62d25b8be22b0928e365848c688 100644 (file)
@@ -11,7 +11,7 @@ defmodule Pleroma.HTTP.Tzdata do
 
   @impl true
   def get(url, headers, options) do
-    options = Keyword.put_new(options, :adapter, pool: :default)
+    options = Keyword.put_new(options, :pool, :default)
 
     with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers, options) do
       {:ok, {env.status, env.headers, env.body}}
@@ -20,7 +20,7 @@ defmodule Pleroma.HTTP.Tzdata do
 
   @impl true
   def head(url, headers, options) do
-    options = Keyword.put_new(options, :adapter, pool: :default)
+    options = Keyword.put_new(options, :pool, :default)
 
     with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers, options) do
       {:ok, {env.status, env.headers}}
index c1825f81044d7a5ca6afb84ccd4d6b49eac50298..8868a910e3da89326eb6c589326d41c35d7e2274 100644 (file)
@@ -648,4 +648,16 @@ defmodule Pleroma.Notification do
     )
     |> Repo.one()
   end
+
+  @spec mark_context_as_read(User.t(), String.t()) :: {integer(), nil | [term()]}
+  def mark_context_as_read(%User{id: id}, context) do
+    from(
+      n in Notification,
+      join: a in assoc(n, :activity),
+      where: n.user_id == ^id,
+      where: n.seen == false,
+      where: fragment("?->>'context'", a.data) == ^context
+    )
+    |> Repo.update_all(set: [seen: true])
+  end
 end
index 374d8704ad966bab3512aa538f85243976ac705a..1de2ce6c3fd261a103f6d0906f7fd86d717d8d28 100644 (file)
@@ -36,8 +36,7 @@ defmodule Pleroma.Object.Fetcher do
   defp reinject_object(%Object{data: %{"type" => "Question"}} = object, new_data) do
     Logger.debug("Reinjecting object #{new_data["id"]}")
 
-    with new_data <- Transmogrifier.fix_object(new_data),
-         data <- maybe_reinject_internal_fields(object, new_data),
+    with data <- maybe_reinject_internal_fields(object, new_data),
          {:ok, data, _} <- ObjectValidator.validate(data, %{}),
          changeset <- Object.change(object, %{data: data}),
          changeset <- touch_changeset(changeset),
index 9a03f01db7087d1766599908748a062050b14354..e5c9c668baef9bafc9ee6b1658481cc36faf0af7 100644 (file)
@@ -3,12 +3,15 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Stats do
+  use GenServer
+
   import Ecto.Query
+
   alias Pleroma.CounterCache
   alias Pleroma.Repo
   alias Pleroma.User
 
-  use GenServer
+  @interval :timer.seconds(60)
 
   def start_link(_) do
     GenServer.start_link(
@@ -18,6 +21,12 @@ defmodule Pleroma.Stats do
     )
   end
 
+  @impl true
+  def init(_args) do
+    if Pleroma.Config.get(:env) == :test, do: :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
+    {:ok, nil, {:continue, :calculate_stats}}
+  end
+
   @doc "Performs update stats"
   def force_update do
     GenServer.call(__MODULE__, :force_update)
@@ -29,7 +38,11 @@ defmodule Pleroma.Stats do
   end
 
   @doc "Returns stats data"
-  @spec get_stats() :: %{domain_count: integer(), status_count: integer(), user_count: integer()}
+  @spec get_stats() :: %{
+          domain_count: non_neg_integer(),
+          status_count: non_neg_integer(),
+          user_count: non_neg_integer()
+        }
   def get_stats do
     %{stats: stats} = GenServer.call(__MODULE__, :get_state)
 
@@ -44,25 +57,14 @@ defmodule Pleroma.Stats do
     peers
   end
 
-  def init(_args) do
-    {:ok, calculate_stat_data()}
-  end
-
-  def handle_call(:force_update, _from, _state) do
-    new_stats = calculate_stat_data()
-    {:reply, new_stats, new_stats}
-  end
-
-  def handle_call(:get_state, _from, state) do
-    {:reply, state, state}
-  end
-
-  def handle_cast(:run_update, _state) do
-    new_stats = calculate_stat_data()
-
-    {:noreply, new_stats}
-  end
-
+  @spec calculate_stat_data() :: %{
+          peers: list(),
+          stats: %{
+            domain_count: non_neg_integer(),
+            status_count: non_neg_integer(),
+            user_count: non_neg_integer()
+          }
+        }
   def calculate_stat_data do
     peers =
       from(
@@ -97,6 +99,7 @@ defmodule Pleroma.Stats do
     }
   end
 
+  @spec get_status_visibility_count(String.t() | nil) :: map()
   def get_status_visibility_count(instance \\ nil) do
     if is_nil(instance) do
       CounterCache.get_sum()
@@ -104,4 +107,36 @@ defmodule Pleroma.Stats do
       CounterCache.get_by_instance(instance)
     end
   end
+
+  @impl true
+  def handle_continue(:calculate_stats, _) do
+    stats = calculate_stat_data()
+    Process.send_after(self(), :run_update, @interval)
+    {:noreply, stats}
+  end
+
+  @impl true
+  def handle_call(:force_update, _from, _state) do
+    new_stats = calculate_stat_data()
+    {:reply, new_stats, new_stats}
+  end
+
+  @impl true
+  def handle_call(:get_state, _from, state) do
+    {:reply, state, state}
+  end
+
+  @impl true
+  def handle_cast(:run_update, _state) do
+    new_stats = calculate_stat_data()
+
+    {:noreply, new_stats}
+  end
+
+  @impl true
+  def handle_info(:run_update, _) do
+    new_stats = calculate_stat_data()
+    Process.send_after(self(), :run_update, @interval)
+    {:noreply, new_stats}
+  end
 end
diff --git a/lib/pleroma/tesla/middleware/connection_pool.ex b/lib/pleroma/tesla/middleware/connection_pool.ex
new file mode 100644 (file)
index 0000000..056e736
--- /dev/null
@@ -0,0 +1,50 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Tesla.Middleware.ConnectionPool do
+  @moduledoc """
+  Middleware to get/release connections from `Pleroma.Gun.ConnectionPool`
+  """
+
+  @behaviour Tesla.Middleware
+
+  alias Pleroma.Gun.ConnectionPool
+
+  @impl Tesla.Middleware
+  def call(%Tesla.Env{url: url, opts: opts} = env, next, _) do
+    uri = URI.parse(url)
+
+    # Avoid leaking connections when the middleware is called twice
+    # with body_as: :chunks. We assume only the middleware can set
+    # opts[:adapter][:conn]
+    if opts[:adapter][:conn] do
+      ConnectionPool.release_conn(opts[:adapter][:conn])
+    end
+
+    case ConnectionPool.get_conn(uri, opts[:adapter]) do
+      {:ok, conn_pid} ->
+        adapter_opts = Keyword.merge(opts[:adapter], conn: conn_pid, close_conn: false)
+        opts = Keyword.put(opts, :adapter, adapter_opts)
+        env = %{env | opts: opts}
+
+        case Tesla.run(env, next) do
+          {:ok, env} ->
+            unless opts[:adapter][:body_as] == :chunks do
+              ConnectionPool.release_conn(conn_pid)
+              {_, res} = pop_in(env.opts[:adapter][:conn])
+              {:ok, res}
+            else
+              {:ok, env}
+            end
+
+          err ->
+            ConnectionPool.release_conn(conn_pid)
+            err
+        end
+
+      err ->
+        err
+    end
+  end
+end
diff --git a/lib/pleroma/tesla/middleware/follow_redirects.ex b/lib/pleroma/tesla/middleware/follow_redirects.ex
deleted file mode 100644 (file)
index 5a70322..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2015-2020 Tymon Tobolski <https://github.com/teamon/tesla/blob/master/lib/tesla/middleware/follow_redirects.ex>
-# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.HTTP.Middleware.FollowRedirects do
-  @moduledoc """
-  Pool-aware version of https://github.com/teamon/tesla/blob/master/lib/tesla/middleware/follow_redirects.ex
-
-  Follow 3xx redirects
-  ## Options
-  - `:max_redirects` - limit number of redirects (default: `5`)
-  """
-
-  alias Pleroma.Gun.ConnectionPool
-
-  @behaviour Tesla.Middleware
-
-  @max_redirects 5
-  @redirect_statuses [301, 302, 303, 307, 308]
-
-  @impl Tesla.Middleware
-  def call(env, next, opts \\ []) do
-    max = Keyword.get(opts, :max_redirects, @max_redirects)
-
-    redirect(env, next, max)
-  end
-
-  defp redirect(env, next, left) do
-    opts = env.opts[:adapter]
-
-    case Tesla.run(env, next) do
-      {:ok, %{status: status} = res} when status in @redirect_statuses and left > 0 ->
-        release_conn(opts)
-
-        case Tesla.get_header(res, "location") do
-          nil ->
-            {:ok, res}
-
-          location ->
-            location = parse_location(location, res)
-
-            case get_conn(location, opts) do
-              {:ok, opts} ->
-                %{env | opts: Keyword.put(env.opts, :adapter, opts)}
-                |> new_request(res.status, location)
-                |> redirect(next, left - 1)
-
-              e ->
-                e
-            end
-        end
-
-      {:ok, %{status: status}} when status in @redirect_statuses ->
-        release_conn(opts)
-        {:error, {__MODULE__, :too_many_redirects}}
-
-      {:error, _} = e ->
-        release_conn(opts)
-        e
-
-      other ->
-        unless opts[:body_as] == :chunks do
-          release_conn(opts)
-        end
-
-        other
-    end
-  end
-
-  defp get_conn(location, opts) do
-    uri = URI.parse(location)
-
-    case ConnectionPool.get_conn(uri, opts) do
-      {:ok, conn} ->
-        {:ok, Keyword.merge(opts, conn: conn)}
-
-      e ->
-        e
-    end
-  end
-
-  defp release_conn(opts) do
-    ConnectionPool.release_conn(opts[:conn])
-  end
-
-  # The 303 (See Other) redirect was added in HTTP/1.1 to indicate that the originally
-  # requested resource is not available, however a related resource (or another redirect)
-  # available via GET is available at the specified location.
-  # https://tools.ietf.org/html/rfc7231#section-6.4.4
-  defp new_request(env, 303, location), do: %{env | url: location, method: :get, query: []}
-
-  # The 307 (Temporary Redirect) status code indicates that the target
-  # resource resides temporarily under a different URI and the user agent
-  # MUST NOT change the request method (...)
-  # https://tools.ietf.org/html/rfc7231#section-6.4.7
-  defp new_request(env, 307, location), do: %{env | url: location}
-
-  defp new_request(env, _, location), do: %{env | url: location, query: []}
-
-  defp parse_location("https://" <> _rest = location, _env), do: location
-  defp parse_location("http://" <> _rest = location, _env), do: location
-
-  defp parse_location(location, env) do
-    env.url
-    |> URI.parse()
-    |> URI.merge(location)
-    |> URI.to_string()
-  end
-end
index ce5f4bc4aa687a682231664c2af13b1ab39b6b35..f323fc6edcc0fbec15ab17e4844764c41e0f5c40 100644 (file)
@@ -83,7 +83,7 @@ defmodule Pleroma.User do
   ]
 
   schema "users" do
-    field(:bio, :string)
+    field(:bio, :string, default: "")
     field(:raw_bio, :string)
     field(:email, :string)
     field(:name, :string)
@@ -1587,7 +1587,7 @@ defmodule Pleroma.User do
     # "Right to be forgotten"
     # https://gdpr.eu/right-to-be-forgotten/
     change(user, %{
-      bio: nil,
+      bio: "",
       raw_bio: nil,
       email: nil,
       name: nil,
index 624a508ae3d907438d0df2301cdb1ea458580257..3336214133a9e046ea8ad2a01d793e594602aff1 100644 (file)
@@ -1224,7 +1224,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       name: data["name"],
       follower_address: data["followers"],
       following_address: data["following"],
-      bio: data["summary"],
+      bio: data["summary"] || "",
       actor_type: actor_type,
       also_known_as: Map.get(data, "alsoKnownAs", []),
       public_key: public_key,
diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
new file mode 100644 (file)
index 0000000..ea9c3d3
--- /dev/null
@@ -0,0 +1,56 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
+  alias Pleroma.User
+  @behaviour Pleroma.Web.ActivityPub.MRF
+  @moduledoc "Remove bot posts from federated timeline"
+
+  require Pleroma.Constants
+
+  defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"]
+  defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname)
+
+  defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user)
+
+  @impl true
+  def filter(
+        %{
+          "type" => "Create",
+          "to" => to,
+          "cc" => cc,
+          "actor" => actor,
+          "object" => object
+        } = message
+      ) do
+    user = User.get_cached_by_ap_id(actor)
+    isbot = check_if_bot(user)
+
+    if isbot and Enum.member?(to, Pleroma.Constants.as_public()) do
+      to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
+      cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
+
+      object =
+        object
+        |> Map.put("to", to)
+        |> Map.put("cc", cc)
+
+      message =
+        message
+        |> Map.put("to", to)
+        |> Map.put("cc", cc)
+        |> Map.put("object", object)
+
+      {:ok, message}
+    else
+      {:ok, message}
+    end
+  end
+
+  @impl true
+  def filter(message), do: {:ok, message}
+
+  @impl true
+  def describe, do: {:ok, %{}}
+end
index dfab105a3e4fe6d7616a9b95dfba5cd064186e93..98d5954690f188133095a330de9191cb68a57fed 100644 (file)
@@ -13,22 +13,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
   require Logger
 
   @options [
-    pool: :media
+    pool: :media,
+    recv_timeout: 10_000
   ]
 
   def perform(:prefetch, url) do
     Logger.debug("Prefetching #{inspect(url)}")
 
-    opts =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
-        Keyword.put(@options, :recv_timeout, 10_000)
-      else
-        @options
-      end
-
     url
     |> MediaProxy.url()
-    |> HTTP.get([], adapter: opts)
+    |> HTTP.get([], @options)
   end
 
   def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do
index d1869f18809ad7e8a068246546a9da004b7f841b..1a97c504a90848c93d8aa6789b32cfc2daddab2f 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
   alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+  alias Pleroma.Web.ActivityPub.Transmogrifier
 
   import Ecto.Changeset
 
@@ -33,8 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
     field(:attributedTo, ObjectValidators.ObjectID)
     field(:summary, :string)
     field(:published, ObjectValidators.DateTime)
-    # TODO: Write type
-    field(:emoji, :map, default: %{})
+    field(:emoji, ObjectValidators.Emoji, default: %{})
     field(:sensitive, :boolean, default: false)
     embeds_many(:attachment, AttachmentValidator)
     field(:replies_count, :integer, default: 0)
@@ -83,6 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
     data
     |> CommonFixes.fix_defaults()
     |> CommonFixes.fix_attribution()
+    |> Transmogrifier.fix_emoji()
     |> fix_url()
   end
 
index 91b475393335c9567b08c57132d50b36f649b4f9..6acd4a771df230e4644321e651d2b32132eeb431 100644 (file)
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
     field(:content, ObjectValidators.SafeText)
     field(:actor, ObjectValidators.ObjectID)
     field(:published, ObjectValidators.DateTime)
-    field(:emoji, :map, default: %{})
+    field(:emoji, ObjectValidators.Emoji, default: %{})
 
     embeds_one(:attachment, AttachmentValidator)
   end
index 721749de0c5611d5fe29ccfb43d5732b5ac2c9ca..720213d7327de4ea019f5e1ee6fbe7796f1dae8e 100644 (file)
@@ -11,8 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
       Utils.create_context(data["context"] || data["conversation"])
 
     data
-    |> Map.put_new("context", context)
-    |> Map.put_new("context_id", context_id)
+    |> Map.put("context", context)
+    |> Map.put("context_id", context_id)
   end
 
   def fix_attribution(data) do
index 07e4821a4f5f37023e0b62c5998215b0fadc2d7a..0b4c99dc0286bd87a2ed21fda66b4633e28faf9d 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
   alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+  alias Pleroma.Web.ActivityPub.Transmogrifier
 
   import Ecto.Changeset
 
@@ -39,8 +40,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
 
     field(:attributedTo, ObjectValidators.ObjectID)
     field(:published, ObjectValidators.DateTime)
-    # TODO: Write type
-    field(:emoji, :map, default: %{})
+    field(:emoji, ObjectValidators.Emoji, default: %{})
     field(:sensitive, :boolean, default: false)
     embeds_many(:attachment, AttachmentValidator)
     field(:replies_count, :integer, default: 0)
@@ -74,6 +74,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
     data
     |> CommonFixes.fix_defaults()
     |> CommonFixes.fix_attribution()
+    |> Transmogrifier.fix_emoji()
   end
 
   def changeset(struct, data) do
index 20e73561946996f4861cd409a1e57bd295a986a4..ab4469a59ac05aee6191ab27044074e893245c19 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
   use Ecto.Schema
 
   alias Pleroma.EctoType.ActivityPub.ObjectValidators
+  alias Pleroma.Web.ActivityPub.Transmogrifier
 
   import Ecto.Changeset
 
@@ -32,8 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
     field(:actor, ObjectValidators.ObjectID)
     field(:attributedTo, ObjectValidators.ObjectID)
     field(:published, ObjectValidators.DateTime)
-    # TODO: Write type
-    field(:emoji, :map, default: %{})
+    field(:emoji, ObjectValidators.Emoji, default: %{})
     field(:sensitive, :boolean, default: false)
     # TODO: Write type
     field(:attachment, {:array, :map}, default: [])
@@ -53,7 +53,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
     |> validate_data()
   end
 
+  defp fix(data) do
+    data
+    |> Transmogrifier.fix_emoji()
+  end
+
   def cast_data(data) do
+    data = fix(data)
+
     %__MODULE__{}
     |> cast(data, __schema__(:fields))
   end
index 712047424665ce5729423729d359575ed1397b83..934d3c1ea048624c49c0831b226a8321ea98e5f7 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
   alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
   alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator
+  alias Pleroma.Web.ActivityPub.Transmogrifier
 
   import Ecto.Changeset
 
@@ -35,8 +36,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
     field(:attributedTo, ObjectValidators.ObjectID)
     field(:summary, :string)
     field(:published, ObjectValidators.DateTime)
-    # TODO: Write type
-    field(:emoji, :map, default: %{})
+    field(:emoji, ObjectValidators.Emoji, default: %{})
     field(:sensitive, :boolean, default: false)
     embeds_many(:attachment, AttachmentValidator)
     field(:replies_count, :integer, default: 0)
@@ -85,6 +85,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
     data
     |> CommonFixes.fix_defaults()
     |> CommonFixes.fix_attribution()
+    |> Transmogrifier.fix_emoji()
     |> fix_closed()
   end
 
index 76298c4a0b840542f6906c447d1283f472d81ea9..0831efadcca99545fedf939ebd3e770ad2e97305 100644 (file)
@@ -318,9 +318,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         Map.put(mapping, name, data["icon"]["url"])
       end)
 
-    # we merge mastodon and pleroma emoji into a single mapping, to allow for both wire formats
-    emoji = Map.merge(object["emoji"] || %{}, emoji)
-
     Map.put(object, "emoji", emoji)
   end
 
index c88ed5dd0ebd51cb29720a07ebc571474e51a5d4..15039052e1e5e0f12617276eea7fa04596b2af8d 100644 (file)
@@ -114,7 +114,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
       description: "Add accounts to the given list.",
       operationId: "ListController.add_to_list",
       parameters: [id_param()],
-      requestBody: add_remove_accounts_request(),
+      requestBody: add_remove_accounts_request(true),
       security: [%{"oAuth" => ["write:lists"]}],
       responses: %{
         200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
@@ -127,8 +127,16 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
       tags: ["Lists"],
       summary: "Remove accounts from list",
       operationId: "ListController.remove_from_list",
-      parameters: [id_param()],
-      requestBody: add_remove_accounts_request(),
+      parameters: [
+        id_param(),
+        Operation.parameter(
+          :account_ids,
+          :query,
+          %Schema{type: :array, items: %Schema{type: :string}},
+          "Array of account IDs"
+        )
+      ],
+      requestBody: add_remove_accounts_request(false),
       security: [%{"oAuth" => ["write:lists"]}],
       responses: %{
         200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
@@ -171,7 +179,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
     )
   end
 
-  defp add_remove_accounts_request do
+  defp add_remove_accounts_request(required) when is_boolean(required) do
     request_body(
       "Parameters",
       %Schema{
@@ -180,9 +188,9 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
         properties: %{
           account_ids: %Schema{type: :array, description: "Array of account IDs", items: FlakeID}
         },
-        required: [:account_ids]
+        required: required && [:account_ids]
       },
-      required: true
+      required: required
     )
   end
 end
index 200ca03dcd7a83bfdc1b56c583df1bde8f36d059..c611b3e0916dd83485bb65c3a04cd789d5532c6c 100644 (file)
@@ -68,7 +68,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
     nickname = value([registration_attrs["nickname"], Registration.nickname(registration)])
     email = value([registration_attrs["email"], Registration.email(registration)])
     name = value([registration_attrs["name"], Registration.name(registration)]) || nickname
-    bio = value([registration_attrs["bio"], Registration.description(registration)])
+    bio = value([registration_attrs["bio"], Registration.description(registration)]) || ""
 
     random_password = :crypto.strong_rand_bytes(64) |> Base.encode64()
 
index 5ad2b91c265c836b20e8f4840729db768a38a73b..4ab533658e41342571c72d1ee64e2d9650c55908 100644 (file)
@@ -452,7 +452,8 @@ defmodule Pleroma.Web.CommonAPI do
   end
 
   def add_mute(user, activity) do
-    with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
+    with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]),
+         _ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do
       {:ok, activity}
     else
       {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
index acdc76fd217af2fbf9678d5b1dafe5e52822d928..5daeaa78002ab47fe45363d4e28fe362f27b4988 100644 (file)
@@ -74,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
 
   # DELETE /api/v1/lists/:id/accounts
   def remove_from_list(
-        %{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn,
+        %{assigns: %{list: list}, params: %{account_ids: account_ids}} = conn,
         _
       ) do
     Enum.each(account_ids, fn account_id ->
@@ -86,6 +86,10 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
     json(conn, %{})
   end
 
+  def remove_from_list(%{body_params: params} = conn, _) do
+    remove_from_list(%{conn | params: params}, %{})
+  end
+
   defp list_by_id_and_user(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do
     case Pleroma.List.get(id, user) do
       %Pleroma.List{} = list -> assign(conn, :list, list)
index 864c0417f14a2694ac9cb0583d118ceb4d4806c4..d2a30a5483022d426da265b9a43bf2a931c48d77 100644 (file)
@@ -245,7 +245,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       followers_count: followers_count,
       following_count: following_count,
       statuses_count: user.note_count,
-      note: user.bio || "",
+      note: user.bio,
       url: user.uri || user.ap_id,
       avatar: image,
       avatar_static: image,
index 68c871e71b19aa750bdeb7e67c6105b9bfa47c59..bb1b23208f8a112e96d923aeb13d7dc4287622e3 100644 (file)
@@ -61,7 +61,7 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
 
   @impl Provider
   def build_tags(%{user: user}) do
-    with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do
+    with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
       [
         {:meta,
          [
index 5d08ce422ea11b8e59a83b162c2fd3af3eb09ffe..df34b033f6a3fbd527119967f719dd3b69e33f46 100644 (file)
@@ -40,7 +40,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
 
   @impl Provider
   def build_tags(%{user: user}) do
-    with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do
+    with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
       [
         title_tag(user),
         {:meta, [property: "twitter:description", content: truncated_bio], []},
index 1f2e953f761cc4e915263946d13e9ad97a3cf97d..e8a1746d46a82d459dedc10799c58e92f4af2063 100644 (file)
@@ -149,9 +149,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
       from(c in Chat,
         where: c.user_id == ^user_id,
         where: c.recipient not in ^blocked_ap_ids,
-        order_by: [desc: c.updated_at],
-        inner_join: u in User,
-        on: u.ap_id == c.recipient
+        order_by: [desc: c.updated_at]
       )
       |> Repo.all()
 
index 8e2b515086beb211b8781823c03bf6334f72c805..28f75b18d624d297559f68e88c24b1fceea57d35 100644 (file)
@@ -5,7 +5,8 @@
 defmodule Pleroma.Web.RelMe do
   @options [
     pool: :media,
-    max_body: 2_000_000
+    max_body: 2_000_000,
+    recv_timeout: 2_000
   ]
 
   if Pleroma.Config.get(:env) == :test do
@@ -23,18 +24,8 @@ defmodule Pleroma.Web.RelMe do
   def parse(_), do: {:error, "No URL provided"}
 
   defp parse_url(url) do
-    opts =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
-        Keyword.merge(@options,
-          recv_timeout: 2_000,
-          with_body: true
-        )
-      else
-        @options
-      end
-
     with {:ok, %Tesla.Env{body: html, status: status}} when status in 200..299 <-
-           Pleroma.HTTP.get(url, [], adapter: opts),
+           Pleroma.HTTP.get(url, [], @options),
          {:ok, html_tree} <- Floki.parse_document(html),
          data <-
            Floki.attribute(html_tree, "link[rel~=me]", "href") ++
index 752ca9f8137c6cf6321cc96e0a7bef49e23b7a49..bd7f03cbeba8c3bf55bf86341e47440ae17464d9 100644 (file)
@@ -9,14 +9,15 @@ defmodule Pleroma.Web.RichMedia.Helpers do
   alias Pleroma.Object
   alias Pleroma.Web.RichMedia.Parser
 
-  @rich_media_options [
+  @options [
     pool: :media,
-    max_body: 2_000_000
+    max_body: 2_000_000,
+    recv_timeout: 2_000
   ]
 
   @spec validate_page_url(URI.t() | binary()) :: :ok | :error
   defp validate_page_url(page_url) when is_binary(page_url) do
-    validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
+    validate_tld = Config.get([Pleroma.Formatter, :validate_tld])
 
     page_url
     |> Linkify.Parser.url?(validate_tld: validate_tld)
@@ -86,16 +87,6 @@ defmodule Pleroma.Web.RichMedia.Helpers do
   def rich_media_get(url) do
     headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
 
-    options =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
-        Keyword.merge(@rich_media_options,
-          recv_timeout: 2_000,
-          with_body: true
-        )
-      else
-        @rich_media_options
-      end
-
-    Pleroma.HTTP.get(url, headers, adapter: options)
+    Pleroma.HTTP.get(url, headers, @options)
   end
 end
index e98c743caa7d4be1214b4a1157fc365d2305f571..5727fda189bf39f4ac501461504ff247f1f86fa7 100644 (file)
@@ -21,8 +21,13 @@ defmodule Pleroma.Web.RichMedia.Parser do
            {:ok, _} <- set_ttl_based_on_image(data, url) do
         {:ok, data}
       else
+        {:error, {:invalid_metadata, data}} = e ->
+          Logger.debug(fn -> "Incomplete or invalid metadata for #{url}: #{inspect(data)}" end)
+          e
+
         error ->
-          Logger.error(fn -> "Rich media error: #{inspect(error)}" end)
+          Logger.error(fn -> "Rich media error for #{url}: #{inspect(error)}" end)
+          error
       end
     end
 
@@ -90,7 +95,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
     end)
   end
 
-  defp parse_url(url) do
+  def parse_url(url) do
     with {:ok, %Tesla.Env{body: html}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url),
          {:ok, html} <- Floki.parse_document(html) do
       html
@@ -116,7 +121,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
   end
 
   defp check_parsed_data(data) do
-    {:error, "Found metadata was invalid or incomplete: #{inspect(data)}"}
+    {:error, {:invalid_metadata, data}}
   end
 
   defp clean_parsed_data(data) do
diff --git a/lib/pleroma/workers/cron/stats_worker.ex b/lib/pleroma/workers/cron/stats_worker.ex
deleted file mode 100644 (file)
index 6a79540..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.Cron.StatsWorker do
-  @moduledoc """
-  The worker to update peers statistics.
-  """
-
-  use Oban.Worker, queue: "background"
-
-  @impl Oban.Worker
-  def perform(_job) do
-    Pleroma.Stats.do_collect()
-    :ok
-  end
-end
diff --git a/mix.exs b/mix.exs
index 51e05965e20e88b35c7b6686de362412fb9de0cb..9499aab2ded5337f7abfbcd97366620a0b850dbd 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
   def project do
     [
       app: :pleroma,
-      version: version("2.1.1"),
+      version: version("2.1.50"),
       elixir: "~> 1.9",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
@@ -134,7 +134,9 @@ defmodule Pleroma.Mixfile do
       {:cachex, "~> 3.2"},
       {:poison, "~> 3.0", override: true},
       {:tesla,
-       github: "teamon/tesla", ref: "af3707078b10793f6a534938e56b963aff82fe3c", override: true},
+       git: "https://github.com/teamon/tesla/",
+       ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30",
+       override: true},
       {:castore, "~> 0.1"},
       {:cowlib, "~> 2.9", override: true},
       {:gun,
index b97dd63423bb8689d2426660f2ed57c888614d1a..c4f9cd28c7b29d552889a91828633178f65e798f 100644 (file)
--- a/mix.lock
+++ b/mix.lock
   "swoosh": {:hex, :swoosh, "1.0.0", "c547cfc83f30e12d5d1fdcb623d7de2c2e29a5becfc68bf8f42ba4d23d2c2756", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "b3b08e463f876cb6167f7168e9ad99a069a724e124bcee61847e0e1ed13f4a0d"},
   "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
   "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
-  "tesla": {:git, "https://github.com/teamon/tesla.git", "af3707078b10793f6a534938e56b963aff82fe3c", [ref: "af3707078b10793f6a534938e56b963aff82fe3c"]},
+  "tesla": {:git, "https://github.com/teamon/tesla/", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]},
   "timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
   "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
   "tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
diff --git a/priv/repo/migrations/20200831142509_chat_constraints.exs b/priv/repo/migrations/20200831142509_chat_constraints.exs
new file mode 100644 (file)
index 0000000..868a40a
--- /dev/null
@@ -0,0 +1,22 @@
+defmodule Pleroma.Repo.Migrations.ChatConstraints do
+  use Ecto.Migration
+
+  def change do
+    remove_orphans = """
+    delete from chats where not exists(select id from users where ap_id = chats.recipient);
+    """
+
+    execute(remove_orphans)
+
+    drop(constraint(:chats, "chats_user_id_fkey"))
+
+    alter table(:chats) do
+      modify(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
+
+      modify(
+        :recipient,
+        references(:users, column: :ap_id, type: :string, on_delete: :delete_all)
+      )
+    end
+  end
+end
diff --git a/priv/repo/migrations/20200901061256_ensure_bio_is_string.exs b/priv/repo/migrations/20200901061256_ensure_bio_is_string.exs
new file mode 100644 (file)
index 0000000..0e3bb3c
--- /dev/null
@@ -0,0 +1,7 @@
+defmodule Pleroma.Repo.Migrations.EnsureBioIsString do
+  use Ecto.Migration
+
+  def change do
+    execute("update users set bio = '' where bio is null", "")
+  end
+end
diff --git a/priv/repo/migrations/20200901061637_bio_set_not_null.exs b/priv/repo/migrations/20200901061637_bio_set_not_null.exs
new file mode 100644 (file)
index 0000000..e3a67d4
--- /dev/null
@@ -0,0 +1,10 @@
+defmodule Pleroma.Repo.Migrations.BioSetNotNull do
+  use Ecto.Migration
+
+  def change do
+    execute(
+      "alter table users alter column bio set not null",
+      "alter table users alter column bio drop not null"
+    )
+  end
+end
diff --git a/priv/repo/migrations/20200905082737_rename_await_up_timeout_in_connections_pool.exs b/priv/repo/migrations/20200905082737_rename_await_up_timeout_in_connections_pool.exs
new file mode 100644 (file)
index 0000000..22c4066
--- /dev/null
@@ -0,0 +1,13 @@
+defmodule Pleroma.Repo.Migrations.RenameAwaitUpTimeoutInConnectionsPool do
+  use Ecto.Migration
+
+  def change do
+    with %Pleroma.ConfigDB{} = config <-
+           Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: :connections_pool}),
+         {timeout, value} when is_integer(timeout) <- Keyword.pop(config.value, :await_up_timeout) do
+      config
+      |> Ecto.Changeset.change(value: Keyword.put(value, :connect_timeout, timeout))
+      |> Pleroma.Repo.update()
+    end
+  end
+end
diff --git a/priv/repo/migrations/20200905091427_rename_timeout_in_pools.exs b/priv/repo/migrations/20200905091427_rename_timeout_in_pools.exs
new file mode 100644 (file)
index 0000000..bb2f50e
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.RenameTimeoutInPools do
+  use Ecto.Migration
+
+  def change do
+    with %Pleroma.ConfigDB{} = config <-
+           Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: :pools}) do
+      updated_value =
+        Enum.map(config.value, fn {pool, pool_value} ->
+          with {timeout, value} when is_integer(timeout) <- Keyword.pop(pool_value, :timeout) do
+            {pool, Keyword.put(value, :recv_timeout, timeout)}
+          end
+        end)
+
+      config
+      |> Ecto.Changeset.change(value: updated_value)
+      |> Pleroma.Repo.update()
+    end
+  end
+end
diff --git a/priv/repo/migrations/20200906072147_remove_cron_stats_worker_from_oban_config.exs b/priv/repo/migrations/20200906072147_remove_cron_stats_worker_from_oban_config.exs
new file mode 100644 (file)
index 0000000..022f21d
--- /dev/null
@@ -0,0 +1,19 @@
+defmodule Pleroma.Repo.Migrations.RemoveCronStatsWorkerFromObanConfig do
+  use Ecto.Migration
+
+  def change do
+    with %Pleroma.ConfigDB{} = config <-
+           Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Oban}),
+         crontab when is_list(crontab) <- config.value[:crontab],
+         index when is_integer(index) <-
+           Enum.find_index(crontab, fn {_, worker} ->
+             worker == Pleroma.Workers.Cron.StatsWorker
+           end) do
+      updated_value = Keyword.put(config.value, :crontab, List.delete_at(crontab, index))
+
+      config
+      |> Ecto.Changeset.change(value: updated_value)
+      |> Pleroma.Repo.update()
+    end
+  end
+end
index 332f2180a1d5e92dc0d837cc1b9f9da8ba7525e2..9e8a9ebf01abcca2ba80528c72ecef060e79afc8 100644 (file)
@@ -26,6 +26,28 @@ defmodule Pleroma.ChatTest do
       assert chat.id
     end
 
+    test "deleting the user deletes the chat" do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
+
+      Repo.delete(user)
+
+      refute Chat.get_by_id(chat.id)
+    end
+
+    test "deleting the recipient deletes the chat" do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
+
+      Repo.delete(other_user)
+
+      refute Chat.get_by_id(chat.id)
+    end
+
     test "it returns and bumps a chat for a user and recipient if it already exists" do
       user = insert(:user)
       other_user = insert(:user)
index 555661a715481539df6e333df6d60e33c0e80df5..e22052404c899bdc3b92618361d7ae7b93239ea4 100644 (file)
@@ -4,12 +4,15 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
 
   import ExUnit.CaptureLog
 
+  alias Pleroma.Config
+  alias Pleroma.Config.DeprecationWarnings
+
   test "check_old_mrf_config/0" do
     clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy)
     clear_config([:instance, :mrf_transparency], true)
     clear_config([:instance, :mrf_transparency_exclusions], [])
 
-    assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_old_mrf_config() end) =~
+    assert capture_log(fn -> DeprecationWarnings.check_old_mrf_config() end) =~
              """
              !!!DEPRECATION WARNING!!!
              Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later:
@@ -44,22 +47,66 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
     ]
 
     assert capture_log(fn ->
-             Pleroma.Config.DeprecationWarnings.move_namespace_and_warn(
+             DeprecationWarnings.move_namespace_and_warn(
                config_map,
                "Warning preface"
              )
            end) =~ "Warning preface\n error :key\n error :key2\n error :key3"
 
-    assert Pleroma.Config.get(new_group1) == 1
-    assert Pleroma.Config.get(new_group2) == 2
-    assert Pleroma.Config.get(new_group3) == 3
+    assert Config.get(new_group1) == 1
+    assert Config.get(new_group2) == 2
+    assert Config.get(new_group3) == 3
   end
 
   test "check_media_proxy_whitelist_config/0" do
     clear_config([:media_proxy, :whitelist], ["https://example.com", "example2.com"])
 
     assert capture_log(fn ->
-             Pleroma.Config.DeprecationWarnings.check_media_proxy_whitelist_config()
+             DeprecationWarnings.check_media_proxy_whitelist_config()
            end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option"
   end
+
+  describe "check_gun_pool_options/0" do
+    test "await_up_timeout" do
+      config = Config.get(:connections_pool)
+      clear_config(:connections_pool, Keyword.put(config, :await_up_timeout, 5_000))
+
+      assert capture_log(fn ->
+               DeprecationWarnings.check_gun_pool_options()
+             end) =~
+               "Your config is using old setting name `await_up_timeout` instead of `connect_timeout`"
+    end
+
+    test "pool timeout" do
+      old_config = [
+        federation: [
+          size: 50,
+          max_waiting: 10,
+          timeout: 10_000
+        ],
+        media: [
+          size: 50,
+          max_waiting: 10,
+          timeout: 10_000
+        ],
+        upload: [
+          size: 25,
+          max_waiting: 5,
+          timeout: 15_000
+        ],
+        default: [
+          size: 10,
+          max_waiting: 2,
+          timeout: 5_000
+        ]
+      ]
+
+      clear_config(:pools, old_config)
+
+      assert capture_log(fn ->
+               DeprecationWarnings.check_gun_pool_options()
+             end) =~
+               "Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings"
+    end
+  end
 end
index f09d8d31a689f69fc52a211cc538695fbc599ede..74bf785b0ef0dc1991b8e1fab366c421a9fe566b 100644 (file)
@@ -4,7 +4,10 @@
 
 defmodule Pleroma.StatsTest do
   use Pleroma.DataCase
+
   import Pleroma.Factory
+
+  alias Pleroma.Stats
   alias Pleroma.Web.CommonAPI
 
   describe "user count" do
@@ -13,7 +16,7 @@ defmodule Pleroma.StatsTest do
       _internal = insert(:user, local: true, nickname: nil)
       _internal = Pleroma.Web.ActivityPub.Relay.get_actor()
 
-      assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data())
+      assert match?(%{stats: %{user_count: 1}}, Stats.calculate_stat_data())
     end
   end
 
@@ -47,23 +50,23 @@ defmodule Pleroma.StatsTest do
       end)
 
       assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} =
-               Pleroma.Stats.get_status_visibility_count()
+               Stats.get_status_visibility_count()
     end
 
     test "on status delete" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
-      assert %{"public" => 1} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 1} = Stats.get_status_visibility_count()
       CommonAPI.delete(activity.id, user)
-      assert %{"public" => 0} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 0} = Stats.get_status_visibility_count()
     end
 
     test "on status visibility update" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
-      assert %{"public" => 1, "private" => 0} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 1, "private" => 0} = Stats.get_status_visibility_count()
       {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"})
-      assert %{"public" => 0, "private" => 1} = Pleroma.Stats.get_status_visibility_count()
+      assert %{"public" => 0, "private" => 1} = Stats.get_status_visibility_count()
     end
 
     test "doesn't count unrelated activities" do
@@ -75,7 +78,7 @@ defmodule Pleroma.StatsTest do
       CommonAPI.repeat(activity.id, other_user)
 
       assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} =
-               Pleroma.Stats.get_status_visibility_count()
+               Stats.get_status_visibility_count()
     end
   end
 
@@ -110,10 +113,10 @@ defmodule Pleroma.StatsTest do
       end)
 
       assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} =
-               Pleroma.Stats.get_status_visibility_count(local_instance)
+               Stats.get_status_visibility_count(local_instance)
 
       assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} =
-               Pleroma.Stats.get_status_visibility_count(instance2)
+               Stats.get_status_visibility_count(instance2)
     end
   end
 end
index 0ca2b9a28038f893d2b321d463a6bc41cad26b8b..022ae51be1a7cb8864906889b26c5b39a782417e 100644 (file)
@@ -48,11 +48,18 @@ defmodule Pleroma.FrontendTest do
       }
     })
 
+    folder = Path.join([@dir, "frontends", "pleroma", "fantasy"])
+    previously_existing = Path.join([folder, "temp"])
+    File.mkdir_p!(folder)
+    File.write!(previously_existing, "yey")
+    assert File.exists?(previously_existing)
+
     capture_io(fn ->
       Frontend.run(["install", "pleroma", "--file", "test/fixtures/tesla_mock/frontend.zip"])
     end)
 
-    assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
+    assert File.exists?(Path.join([folder, "test.txt"]))
+    refute File.exists?(previously_existing)
   end
 
   test "it downloads and unzips unknown frontends" do
index 3cf248659c40f962342069df2014f8bbd460829b..50f72549eeab1a61a683d479b27f20f185f3ed3a 100644 (file)
@@ -1466,7 +1466,7 @@ defmodule Pleroma.UserTest do
     user = User.get_by_id(user.id)
 
     assert %User{
-             bio: nil,
+             bio: "",
              raw_bio: nil,
              email: nil,
              name: nil,
diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs
new file mode 100644 (file)
index 0000000..86dd9dd
--- /dev/null
@@ -0,0 +1,60 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+
+  alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy
+  @public "https://www.w3.org/ns/activitystreams#Public"
+
+  defp generate_messages(actor) do
+    {%{
+       "actor" => actor.ap_id,
+       "type" => "Create",
+       "object" => %{},
+       "to" => [@public, "f"],
+       "cc" => [actor.follower_address, "d"]
+     },
+     %{
+       "actor" => actor.ap_id,
+       "type" => "Create",
+       "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]},
+       "to" => ["f", actor.follower_address],
+       "cc" => ["d", @public]
+     }}
+  end
+
+  test "removes from the federated timeline by nickname heuristics 1" do
+    actor = insert(:user, %{nickname: "annoying_ebooks@example.com"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by nickname heuristics 2" do
+    actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by actor type Application" do
+    actor = insert(:user, %{actor_type: "Application"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+
+  test "removes from the federated timeline by actor type Service" do
+    actor = insert(:user, %{actor_type: "Service"})
+
+    {message, except_message} = generate_messages(actor)
+
+    assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message}
+  end
+end
index 50bf03515dff31ab30905a920d1bc606e1b655c3..16e4808e59708e415dba4ec59ffe8807befdae1b 100644 (file)
@@ -69,6 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
       assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
 
       assert Map.put(valid_chat_message, "attachment", nil) == object
+      assert match?(%{"firefox" => _}, object["emoji"])
     end
 
     test "validates for a basic object with an attachment", %{
index c82361828883ef0b17fb7ede4f46a6194b52ec7d..74ee7954382592231a5471467336e05878f2c345 100644 (file)
@@ -106,6 +106,57 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do
     assert Enum.sort(object.data["oneOf"]) == Enum.sort(options)
   end
 
+  test "Mastodon Question activity with custom emojis" do
+    options = [
+      %{
+        "type" => "Note",
+        "name" => ":blobcat:",
+        "replies" => %{"totalItems" => 0, "type" => "Collection"}
+      },
+      %{
+        "type" => "Note",
+        "name" => ":blobfox:",
+        "replies" => %{"totalItems" => 0, "type" => "Collection"}
+      }
+    ]
+
+    tag = [
+      %{
+        "icon" => %{
+          "type" => "Image",
+          "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png"
+        },
+        "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png",
+        "name" => ":blobcat:",
+        "type" => "Emoji",
+        "updated" => "1970-01-01T00:00:00Z"
+      },
+      %{
+        "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"},
+        "id" => "https://blob.cat/emoji/blobfox/blobfox.png",
+        "name" => ":blobfox:",
+        "type" => "Emoji",
+        "updated" => "1970-01-01T00:00:00Z"
+      }
+    ]
+
+    data =
+      File.read!("test/fixtures/mastodon-question-activity.json")
+      |> Poison.decode!()
+      |> Kernel.put_in(["object", "oneOf"], options)
+      |> Kernel.put_in(["object", "tag"], tag)
+
+    {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
+    object = Object.normalize(activity, false)
+
+    assert object.data["oneOf"] == options
+
+    assert object.data["emoji"] == %{
+             "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png",
+             "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png"
+           }
+  end
+
   test "returns an error if received a second time" do
     data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!()
 
index dbf478edf0dd2c9fb87299fb7a5595774d87726a..3bc88c6a913bb7e26eaac1b576bd8a00e0c3613a 100644 (file)
@@ -203,7 +203,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         assert user.note_count == 0
         assert user.follower_count == 0
         assert user.following_count == 0
-        assert user.bio == nil
+        assert user.bio == ""
         assert user.name == nil
 
         assert called(Pleroma.Web.Federator.publish(:_))
index 4ba6232dc7dafe2b3f59fb13e49ec7c95f5b9149..800db9a207207921b31b6da87e4433abe6519ee8 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.CommonAPITest do
   alias Pleroma.Conversation.Participation
   alias Pleroma.Notification
   alias Pleroma.Object
+  alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
@@ -18,6 +19,7 @@ defmodule Pleroma.Web.CommonAPITest do
 
   import Pleroma.Factory
   import Mock
+  import Ecto.Query, only: [from: 2]
 
   require Pleroma.Constants
 
@@ -808,6 +810,69 @@ defmodule Pleroma.Web.CommonAPITest do
       [user: user, activity: activity]
     end
 
+    test "marks notifications as read after mute" do
+      author = insert(:user)
+      activity = insert(:note_activity, user: author)
+
+      friend1 = insert(:user)
+      friend2 = insert(:user)
+
+      {:ok, reply_activity} =
+        CommonAPI.post(
+          friend2,
+          %{
+            status: "@#{author.nickname} @#{friend1.nickname} test reply",
+            in_reply_to_status_id: activity.id
+          }
+        )
+
+      {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
+      {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
+
+      assert Repo.aggregate(
+               from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
+               :count
+             ) == 1
+
+      unread_notifications =
+        Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
+
+      assert Enum.any?(unread_notifications, fn n ->
+               n.type == "favourite" && n.activity_id == favorite_activity.id
+             end)
+
+      assert Enum.any?(unread_notifications, fn n ->
+               n.type == "reblog" && n.activity_id == repeat_activity.id
+             end)
+
+      assert Enum.any?(unread_notifications, fn n ->
+               n.type == "mention" && n.activity_id == reply_activity.id
+             end)
+
+      {:ok, _} = CommonAPI.add_mute(author, activity)
+      assert CommonAPI.thread_muted?(author, activity)
+
+      assert Repo.aggregate(
+               from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
+               :count
+             ) == 1
+
+      read_notifications =
+        Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
+
+      assert Enum.any?(read_notifications, fn n ->
+               n.type == "favourite" && n.activity_id == favorite_activity.id
+             end)
+
+      assert Enum.any?(read_notifications, fn n ->
+               n.type == "reblog" && n.activity_id == repeat_activity.id
+             end)
+
+      assert Enum.any?(read_notifications, fn n ->
+               n.type == "mention" && n.activity_id == reply_activity.id
+             end)
+    end
+
     test "add mute", %{user: user, activity: activity} do
       {:ok, _} = CommonAPI.add_mute(user, activity)
       assert CommonAPI.thread_muted?(user, activity)
index 5d4efcebebb69688e8402a587392759001a888b5..dc6ace843f43a69d475bc991601f4649024b6112 100644 (file)
@@ -112,8 +112,6 @@ defmodule Pleroma.Instances.InstanceTest do
   end
 
   test "Returns nil on too long favicon URLs" do
-    clear_config([:instances_favicons, :enabled], true)
-
     long_favicon_url =
       "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png"
 
index 57a9ef4a44ddf9bf97814e88a7355d2cefe19c0e..091ec006c67456790ca140e9fe1be4fda7fa4d93 100644 (file)
@@ -67,7 +67,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
     assert following == [other_user.follower_address]
   end
 
-  test "removing users from a list" do
+  test "removing users from a list, body params" do
     %{user: user, conn: conn} = oauth_access(["write:lists"])
     other_user = insert(:user)
     third_user = insert(:user)
@@ -85,6 +85,24 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
     assert following == [third_user.follower_address]
   end
 
+  test "removing users from a list, query params" do
+    %{user: user, conn: conn} = oauth_access(["write:lists"])
+    other_user = insert(:user)
+    third_user = insert(:user)
+    {:ok, list} = Pleroma.List.create("name", user)
+    {:ok, list} = Pleroma.List.follow(list, other_user)
+    {:ok, list} = Pleroma.List.follow(list, third_user)
+
+    assert %{} ==
+             conn
+             |> put_req_header("content-type", "application/json")
+             |> delete("/api/v1/lists/#{list.id}/accounts?account_ids[]=#{other_user.id}")
+             |> json_response_and_validate_schema(:ok)
+
+    %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
+    assert following == [third_user.follower_address]
+  end
+
   test "listing users in a list" do
     %{user: user, conn: conn} = oauth_access(["read:lists"])
     other_user = insert(:user)
index 68a5d0091ba34ccf7e46a880bfd71a7c2eed8d4b..9f22f9dcf99e7a2a2445c8739583098cd595d4d1 100644 (file)
@@ -116,9 +116,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
     end
   end
 
-  test "Favicon when :instance_favicons is enabled" do
-  end
-
   test "Represent the user account for the account owner" do
     user = insert(:user)
 
index 1e09cbf842415776035baca2270c85e9df90a369..21ae35f8b4f99d2015846b66a1bad73533907c57 100644 (file)
@@ -66,9 +66,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
   end
 
   test "doesn't just add a title" do
-    assert Parser.parse("http://example.com/non-ogp") ==
-             {:error,
-              "Found metadata was invalid or incomplete: %{\"url\" => \"http://example.com/non-ogp\"}"}
+    assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp")
   end
 
   test "parses ogp" do
index 354d77b562dff4478febf9f695ed1fb38e666191..d164127eec448451706e6d965c7d84e50f42731c 100644 (file)
@@ -594,7 +594,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       user = User.get_by_id(user.id)
       assert user.deactivated == true
       assert user.name == nil
-      assert user.bio == nil
+      assert user.bio == ""
       assert user.password_hash == nil
     end
   end