activity_pub.ex 30.1 KB
Newer Older
1
# Pleroma: A lightweight social networking server
kaniini's avatar
kaniini committed
2
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
defmodule Pleroma.Web.ActivityPub.ActivityPub do
Haelwenn's avatar
Haelwenn committed
6
  alias Pleroma.Activity
7
8
  alias Pleroma.Instances
  alias Pleroma.Notification
Haelwenn's avatar
Haelwenn committed
9
  alias Pleroma.Object
10
  alias Pleroma.Repo
Haelwenn's avatar
Haelwenn committed
11
12
13
  alias Pleroma.Upload
  alias Pleroma.User
  alias Pleroma.Web.ActivityPub.MRF
14
  alias Pleroma.Web.ActivityPub.Transmogrifier
Haelwenn's avatar
Haelwenn committed
15
16
  alias Pleroma.Web.Federator
  alias Pleroma.Web.OStatus
17
  alias Pleroma.Web.WebFinger
18

lain's avatar
lain committed
19
  import Ecto.Query
lain's avatar
lain committed
20
  import Pleroma.Web.ActivityPub.Utils
lain's avatar
lain committed
21
  import Pleroma.Web.ActivityPub.Visibility
22

lain's avatar
lain committed
23
  require Logger
lain's avatar
lain committed
24

lain's avatar
lain committed
25
26
  @httpoison Application.get_env(:pleroma, :httpoison)

27
28
  # For Announce activities, we filter the recipients based on following status for any actors
  # that match actual users.  See issue #164 for more information about why this is necessary.
29
30
31
  defp get_recipients(%{"type" => "Announce"} = data) do
    to = data["to"] || []
    cc = data["cc"] || []
32
33
    actor = User.get_cached_by_ap_id(data["actor"])

34
35
36
37
38
39
40
41
42
43
44
    recipients =
      (to ++ cc)
      |> Enum.filter(fn recipient ->
        case User.get_cached_by_ap_id(recipient) do
          nil ->
            true

          user ->
            User.following?(user, actor)
        end
      end)
45
46

    {recipients, to, cc}
47
48
  end

Maxim Filippov's avatar
Maxim Filippov committed
49
50
51
52
53
54
55
56
  defp get_recipients(%{"type" => "Create"} = data) do
    to = data["to"] || []
    cc = data["cc"] || []
    actor = data["actor"] || []
    recipients = (to ++ cc ++ [actor]) |> Enum.uniq()
    {recipients, to, cc}
  end

57
58
59
60
61
  defp get_recipients(data) do
    to = data["to"] || []
    cc = data["cc"] || []
    recipients = to ++ cc
    {recipients, to, cc}
lain's avatar
lain committed
62
63
  end

64
  defp check_actor_is_active(actor) do
65
66
    if not is_nil(actor) do
      with user <- User.get_cached_by_ap_id(actor),
lain's avatar
lain committed
67
           false <- user.info.deactivated do
68
69
70
71
        :ok
      else
        _e -> :reject
      end
72
73
74
75
76
    else
      :ok
    end
  end

77
  defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
78
79
80
81
82
83
    limit = Pleroma.Config.get([:instance, :remote_limit])
    String.length(content) <= limit
  end

  defp check_remote_limit(_), do: true

84
85
86
87
88
89
90
91
  def increase_note_count_if_public(actor, object) do
    if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
  end

  def decrease_note_count_if_public(actor, object) do
    if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
  end

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  def increase_replies_count_if_reply(%{
        "object" =>
          %{"inReplyTo" => reply_ap_id, "inReplyToStatusId" => reply_status_id} = object,
        "type" => "Create"
      }) do
    if is_public?(object) do
      Activity.increase_replies_count(reply_status_id)
      Object.increase_replies_count(reply_ap_id)
    end
  end

  def increase_replies_count_if_reply(_create_data), do: :noop

  def decrease_replies_count_if_reply(%Object{
        data: %{"inReplyTo" => reply_ap_id, "inReplyToStatusId" => reply_status_id} = object
      }) do
    if is_public?(object) do
      Activity.decrease_replies_count(reply_status_id)
      Object.decrease_replies_count(reply_ap_id)
    end
  end

  def decrease_replies_count_if_reply(_object), do: :noop

lain's avatar
lain committed
116
  def insert(map, local \\ true) when is_map(map) do
117
    with nil <- Activity.normalize(map),
lain's avatar
lain committed
118
         map <- lazy_put_activity_defaults(map),
119
         :ok <- check_actor_is_active(map["actor"]),
120
         {_, true} <- {:remote_limit_error, check_remote_limit(map)},
href's avatar
href committed
121
         {:ok, map} <- MRF.filter(map),
122
         {:ok, object} <- insert_full_object(map) do
123
      {recipients, _, _} = get_recipients(map)
124

lain's avatar
lain committed
125
126
127
128
129
      {:ok, activity} =
        Repo.insert(%Activity{
          data: map,
          local: local,
          actor: map["actor"],
130
          recipients: recipients
lain's avatar
lain committed
131
132
        })

133
134
135
136
137
138
139
140
      # Splice in the child object if we have one.
      activity =
        if !is_nil(object) do
          Map.put(activity, :object, object)
        else
          activity
        end

141
142
143
144
      Task.start(fn ->
        Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
      end)

145
      Notification.create_notifications(activity)
lain's avatar
lain committed
146
      stream_out(activity)
147
      {:ok, activity}
lain's avatar
lain committed
148
149
150
    else
      %Activity{} = activity -> {:ok, activity}
      error -> {:error, error}
lain's avatar
lain committed
151
    end
lain's avatar
lain committed
152
  end
lain's avatar
lain committed
153

lain's avatar
lain committed
154
  def stream_out(activity) do
csaurus's avatar
csaurus committed
155
156
    public = "https://www.w3.org/ns/activitystreams#Public"

157
    if activity.data["type"] in ["Create", "Announce", "Delete"] do
lain's avatar
lain committed
158
      Pleroma.Web.Streamer.stream("user", activity)
eal's avatar
eal committed
159
      Pleroma.Web.Streamer.stream("list", activity)
lain's avatar
lain committed
160

csaurus's avatar
csaurus committed
161
162
      if Enum.member?(activity.data["to"], public) do
        Pleroma.Web.Streamer.stream("public", activity)
163

csaurus's avatar
csaurus committed
164
165
166
        if activity.local do
          Pleroma.Web.Streamer.stream("public:local", activity)
        end
167

168
169
170
171
        if activity.data["type"] in ["Create"] do
          activity.data["object"]
          |> Map.get("tag", [])
          |> Enum.filter(fn tag -> is_bitstring(tag) end)
Haelwenn's avatar
Haelwenn committed
172
          |> Enum.each(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
173

174
175
          if activity.data["object"]["attachment"] != [] do
            Pleroma.Web.Streamer.stream("public:media", activity)
176

177
178
179
            if activity.local do
              Pleroma.Web.Streamer.stream("public:local:media", activity)
            end
180
181
          end
        end
csaurus's avatar
csaurus committed
182
183
184
185
186
      else
        if !Enum.member?(activity.data["cc"] || [], public) &&
             !Enum.member?(
               activity.data["to"],
               User.get_by_ap_id(activity.data["actor"]).follower_address
csaurus's avatar
csaurus committed
187
188
             ),
           do: Pleroma.Web.Streamer.stream("direct", activity)
lain's avatar
lain committed
189
      end
lain's avatar
lain committed
190
191
192
    end
  end

lain's avatar
lain committed
193
194
  def create(%{to: to, actor: actor, context: context, object: object} = params) do
    additional = params[:additional] || %{}
lain's avatar
lain committed
195
196
    # only accept false as false value
    local = !(params[:local] == false)
lain's avatar
lain committed
197
198
    published = params[:published]

lain's avatar
lain committed
199
200
201
202
203
    with create_data <-
           make_create_data(
             %{to: to, actor: actor, published: published, context: context, object: object},
             additional
           ),
lain's avatar
lain committed
204
         {:ok, activity} <- insert(create_data, local),
205
         _ <- increase_replies_count_if_reply(create_data),
206
207
         # Changing note count prior to enqueuing federation task in order to avoid
         # race conditions on updating user.info
208
         {:ok, _actor} <- increase_note_count_if_public(actor, activity),
209
         :ok <- maybe_federate(activity) do
210
211
      {:ok, activity}
    end
lain's avatar
lain committed
212
  end
lain's avatar
lain committed
213

214
  def accept(%{to: to, actor: actor, object: object} = params) do
lain's avatar
lain committed
215
216
    # only accept false as false value
    local = !(params[:local] == false)
217

218
    with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
219
         {:ok, activity} <- insert(data, local),
220
         :ok <- maybe_federate(activity) do
221
222
223
224
      {:ok, activity}
    end
  end

225
226
227
228
  def reject(%{to: to, actor: actor, object: object} = params) do
    # only accept false as false value
    local = !(params[:local] == false)

229
    with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
230
         {:ok, activity} <- insert(data, local),
231
         :ok <- maybe_federate(activity) do
232
233
234
235
      {:ok, activity}
    end
  end

lain's avatar
lain committed
236
  def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
lain's avatar
lain committed
237
238
239
240
241
242
243
244
245
246
    # only accept false as false value
    local = !(params[:local] == false)

    with data <- %{
           "to" => to,
           "cc" => cc,
           "type" => "Update",
           "actor" => actor,
           "object" => object
         },
lain's avatar
lain committed
247
248
249
250
251
252
         {:ok, activity} <- insert(data, local),
         :ok <- maybe_federate(activity) do
      {:ok, activity}
    end
  end

lain's avatar
lain committed
253
  # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
lain's avatar
lain committed
254
255
256
257
258
259
  def like(
        %User{ap_id: ap_id} = user,
        %Object{data: %{"id" => _}} = object,
        activity_id \\ nil,
        local \\ true
      ) do
lain's avatar
lain committed
260
261
262
263
264
265
266
267
268
    with nil <- get_existing_like(ap_id, object),
         like_data <- make_like_data(user, object, activity_id),
         {:ok, activity} <- insert(like_data, local),
         {:ok, object} <- add_like_to_object(activity, object),
         :ok <- maybe_federate(activity) do
      {:ok, activity, object}
    else
      %Activity{} = activity -> {:ok, activity, object}
      error -> {:error, error}
lain's avatar
lain committed
269
    end
lain's avatar
lain committed
270
271
  end

Thog's avatar
Thog committed
272
273
274
275
276
277
278
279
280
281
282
283
284
  def unlike(
        %User{} = actor,
        %Object{} = object,
        activity_id \\ nil,
        local \\ true
      ) do
    with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
         unlike_data <- make_unlike_data(actor, like_activity, activity_id),
         {:ok, unlike_activity} <- insert(unlike_data, local),
         {:ok, _activity} <- Repo.delete(like_activity),
         {:ok, object} <- remove_like_from_object(like_activity, object),
         :ok <- maybe_federate(unlike_activity) do
      {:ok, unlike_activity, like_activity, object}
lain's avatar
lain committed
285
286
    else
      _e -> {:ok, object}
lain's avatar
lain committed
287
288
289
    end
  end

lain's avatar
lain committed
290
291
292
293
  def announce(
        %User{ap_id: _} = user,
        %Object{data: %{"id" => _}} = object,
        activity_id \\ nil,
294
295
        local \\ true,
        public \\ true
lain's avatar
lain committed
296
      ) do
lain's avatar
lain committed
297
    with true <- is_public?(object),
298
         announce_data <- make_announce_data(user, object, activity_id, public),
lain's avatar
lain committed
299
300
301
302
303
304
305
         {:ok, activity} <- insert(announce_data, local),
         {:ok, object} <- add_announce_to_object(activity, object),
         :ok <- maybe_federate(activity) do
      {:ok, activity, object}
    else
      error -> {:error, error}
    end
lain's avatar
lain committed
306
307
  end

308
309
310
  def unannounce(
        %User{} = actor,
        %Object{} = object,
311
312
        activity_id \\ nil,
        local \\ true
313
      ) do
normandy's avatar
normandy committed
314
315
    with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
         unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
316
         {:ok, unannounce_activity} <- insert(unannounce_data, local),
normandy's avatar
normandy committed
317
318
319
         :ok <- maybe_federate(unannounce_activity),
         {:ok, _activity} <- Repo.delete(announce_activity),
         {:ok, object} <- remove_announce_from_object(announce_activity, object) do
320
      {:ok, unannounce_activity, object}
normandy's avatar
normandy committed
321
322
323
324
325
    else
      _e -> {:ok, object}
    end
  end

lain's avatar
lain committed
326
327
328
  def follow(follower, followed, activity_id \\ nil, local \\ true) do
    with data <- make_follow_data(follower, followed, activity_id),
         {:ok, activity} <- insert(data, local),
329
         :ok <- maybe_federate(activity) do
lain's avatar
lain committed
330
331
      {:ok, activity}
    end
lain's avatar
lain committed
332
333
  end

normandy's avatar
normandy committed
334
  def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
lain's avatar
lain committed
335
    with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
336
         {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
normandy's avatar
normandy committed
337
         unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
lain's avatar
lain committed
338
         {:ok, activity} <- insert(unfollow_data, local),
339
         :ok <- maybe_federate(activity) do
lain's avatar
lain committed
340
341
      {:ok, activity}
    end
lain's avatar
lain committed
342
343
  end

lain's avatar
lain committed
344
345
  def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
    user = User.get_cached_by_ap_id(actor)
346
    to = (object.data["to"] || []) ++ (object.data["cc"] || [])
Karen Konou's avatar
Karen Konou committed
347

348
349
350
351
352
353
354
355
    with {:ok, object, activity} <- Object.delete(object),
         data <- %{
           "type" => "Delete",
           "actor" => actor,
           "object" => id,
           "to" => to,
           "deleted_activity_id" => activity && activity.id
         },
lain's avatar
lain committed
356
         {:ok, activity} <- insert(data, local),
357
         _ <- decrease_replies_count_if_reply(object),
358
359
         # Changing note count prior to enqueuing federation task in order to avoid
         # race conditions on updating user.info
360
         {:ok, _actor} <- decrease_note_count_if_public(user, object),
361
         :ok <- maybe_federate(activity) do
lain's avatar
lain committed
362
363
364
365
      {:ok, activity}
    end
  end

normandy's avatar
normandy committed
366
  def block(blocker, blocked, activity_id \\ nil, local \\ true) do
367
368
369
    ap_config = Application.get_env(:pleroma, :activitypub)
    unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked)
    outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
normandy's avatar
normandy committed
370

371
    with true <- unfollow_blocked do
372
      follow_activity = fetch_latest_follow(blocker, blocked)
squidboi's avatar
squidboi committed
373

374
375
376
      if follow_activity do
        unfollow(blocker, blocked, nil, local)
      end
normandy's avatar
normandy committed
377
378
    end

379
380
    with true <- outgoing_blocks,
         block_data <- make_block_data(blocker, blocked, activity_id),
normandy's avatar
normandy committed
381
382
383
         {:ok, activity} <- insert(block_data, local),
         :ok <- maybe_federate(activity) do
      {:ok, activity}
384
    else
squidboi's avatar
squidboi committed
385
      _e -> {:ok, nil}
normandy's avatar
normandy committed
386
387
388
    end
  end

normandy's avatar
normandy committed
389
  def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
normandy's avatar
normandy committed
390
    with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
normandy's avatar
normandy committed
391
         unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
normandy's avatar
normandy committed
392
393
394
395
396
397
         {:ok, activity} <- insert(unblock_data, local),
         :ok <- maybe_federate(activity) do
      {:ok, activity}
    end
  end

minibikini's avatar
Reports    
minibikini committed
398
399
400
401
402
403
404
405
406
407
408
  def flag(
        %{
          actor: actor,
          context: context,
          account: account,
          statuses: statuses,
          content: content
        } = params
      ) do
    # only accept false as false value
    local = !(params[:local] == false)
409
410
411
    forward = !(params[:forward] == false)

    additional = params[:additional] || %{}
minibikini's avatar
Reports    
minibikini committed
412

413
    params = %{
minibikini's avatar
Reports    
minibikini committed
414
415
416
417
418
419
      actor: actor,
      context: context,
      account: account,
      statuses: statuses,
      content: content
    }
420
421
422
423
424

    additional =
      if forward do
        Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
      else
425
        Map.merge(additional, %{"to" => [], "cc" => []})
426
427
428
429
430
      end

    with flag_data <- make_flag_data(params, additional),
         {:ok, activity} <- insert(flag_data, local),
         :ok <- maybe_federate(activity) do
431
432
433
434
435
436
      Enum.each(User.all_superusers(), fn superuser ->
        superuser
        |> Pleroma.AdminEmail.report(actor, account, statuses, content)
        |> Pleroma.Mailer.deliver_async()
      end)

437
438
      {:ok, activity}
    end
minibikini's avatar
Reports    
minibikini committed
439
440
  end

441
  def fetch_activities_for_context(context, opts \\ %{}) do
lain's avatar
lain committed
442
443
    public = ["https://www.w3.org/ns/activitystreams#Public"]

lain's avatar
lain committed
444
445
446
447
448
449
450
    recipients =
      if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public

    query = from(activity in Activity)

    query =
      query
lain's avatar
lain committed
451
      |> restrict_blocked(opts)
lain's avatar
lain committed
452
453
      |> restrict_recipients(recipients, opts["user"])

lain's avatar
lain committed
454
455
456
457
458
459
460
461
462
463
464
465
466
    query =
      from(
        activity in query,
        where:
          fragment(
            "?->>'type' = ? and ?->>'context' = ?",
            activity.data,
            "Create",
            activity.data,
            ^context
          ),
        order_by: [desc: :id]
      )
467
      |> Activity.with_preloaded_object()
lain's avatar
lain committed
468

lain's avatar
lain committed
469
    Repo.all(query)
lain's avatar
lain committed
470
471
  end

lain's avatar
lain committed
472
  def fetch_public_activities(opts \\ %{}) do
lain's avatar
lain committed
473
    q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
lain's avatar
lain committed
474

lain's avatar
lain committed
475
    q
476
    |> restrict_unlisted()
lain's avatar
lain committed
477
478
    |> Repo.all()
    |> Enum.reverse()
lain's avatar
lain committed
479
480
  end

481
482
  @valid_visibilities ~w[direct unlisted public private]

483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
  defp restrict_visibility(query, %{visibility: visibility})
       when is_list(visibility) do
    if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
      query =
        from(
          a in query,
          where:
            fragment(
              "activity_visibility(?, ?, ?) = ANY (?)",
              a.actor,
              a.recipients,
              a.data,
              ^visibility
            )
        )

      Ecto.Adapters.SQL.to_sql(:all, Repo, query)

      query
    else
      Logger.error("Could not restrict visibility to #{visibility}")
    end
  end

lain's avatar
lain committed
507
508
509
510
511
512
513
514
515
516
  defp restrict_visibility(query, %{visibility: visibility})
       when visibility in @valid_visibilities do
    query =
      from(
        a in query,
        where:
          fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
      )

    Ecto.Adapters.SQL.to_sql(:all, Repo, query)
517

lain's avatar
lain committed
518
    query
519
520
521
522
523
524
525
526
527
  end

  defp restrict_visibility(_query, %{visibility: visibility})
       when visibility not in @valid_visibilities do
    Logger.error("Could not restrict visibility to #{visibility}")
  end

  defp restrict_visibility(query, _visibility), do: query

528
529
530
531
532
533
  def fetch_user_activities(user, reading_user, params \\ %{}) do
    params =
      params
      |> Map.put("type", ["Create", "Announce"])
      |> Map.put("actor_id", user.ap_id)
      |> Map.put("whole_db", true)
minibikini's avatar
minibikini committed
534
      |> Map.put("pinned_activity_ids", user.info.pinned_activities)
535
536
537
538
539
540
541
542
543
544
545
546
547

    recipients =
      if reading_user do
        ["https://www.w3.org/ns/activitystreams#Public"] ++
          [reading_user.ap_id | reading_user.following]
      else
        ["https://www.w3.org/ns/activitystreams#Public"]
      end

    fetch_activities(recipients, params)
    |> Enum.reverse()
  end

548
549
  defp restrict_since(query, %{"since_id" => ""}), do: query

lain's avatar
lain committed
550
  defp restrict_since(query, %{"since_id" => since_id}) do
lain's avatar
lain committed
551
    from(activity in query, where: activity.id > ^since_id)
lain's avatar
lain committed
552
  end
lain's avatar
lain committed
553

lain's avatar
lain committed
554
  defp restrict_since(query, _), do: query
lain's avatar
lain committed
555

Haelwenn's avatar
Haelwenn committed
556
557
  defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
       when is_list(tag_reject) and tag_reject != [] do
Haelwenn's avatar
Haelwenn committed
558
559
    from(
      activity in query,
560
      where: fragment(~s(\(not \(? #> '{"object","tag"}'\) \\?| ?\)), activity.data, ^tag_reject)
561
562
563
    )
  end

Haelwenn's avatar
Haelwenn committed
564
565
566
567
568
569
  defp restrict_tag_reject(query, _), do: query

  defp restrict_tag_all(query, %{"tag_all" => tag_all})
       when is_list(tag_all) and tag_all != [] do
    from(
      activity in query,
570
      where: fragment(~s(\(? #> '{"object","tag"}'\) \\?& ?), activity.data, ^tag_all)
Haelwenn's avatar
Haelwenn committed
571
572
573
574
575
    )
  end

  defp restrict_tag_all(query, _), do: query

576
577
578
  defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
    from(
      activity in query,
579
      where: fragment(~s(\(? #> '{"object","tag"}'\) \\?| ?), activity.data, ^tag)
Haelwenn's avatar
Haelwenn committed
580
581
582
    )
  end

583
  defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
lain's avatar
lain committed
584
585
    from(
      activity in query,
586
      where: fragment(~s(? <@ (? #> '{"object","tag"}'\)), ^tag, activity.data)
lain's avatar
lain committed
587
    )
Roger Braun's avatar
Roger Braun committed
588
  end
lain's avatar
lain committed
589

Roger Braun's avatar
Roger Braun committed
590
591
  defp restrict_tag(query, _), do: query

592
593
594
595
596
  defp restrict_to_cc(query, recipients_to, recipients_cc) do
    from(
      activity in query,
      where:
        fragment(
597
598
          "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
          activity.data,
599
          ^recipients_to,
600
601
          activity.data,
          ^recipients_cc
602
603
604
605
        )
    )
  end

feld's avatar
feld committed
606
  defp restrict_recipients(query, [], _user), do: query
lain's avatar
lain committed
607

lain's avatar
lain committed
608
  defp restrict_recipients(query, recipients, nil) do
lain's avatar
lain committed
609
    from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
lain's avatar
lain committed
610
  end
lain's avatar
lain committed
611

lain's avatar
lain committed
612
  defp restrict_recipients(query, recipients, user) do
lain's avatar
lain committed
613
614
    from(
      activity in query,
lain's avatar
lain committed
615
616
      where: fragment("? && ?", ^recipients, activity.recipients),
      or_where: activity.actor == ^user.ap_id
lain's avatar
lain committed
617
    )
lain's avatar
lain committed
618
  end
lain's avatar
lain committed
619

kaniini's avatar
kaniini committed
620
  defp restrict_limit(query, %{"limit" => limit}) do
lain's avatar
lain committed
621
    from(activity in query, limit: ^limit)
kaniini's avatar
kaniini committed
622
  end
lain's avatar
lain committed
623

kaniini's avatar
kaniini committed
624
625
  defp restrict_limit(query, _), do: query

lain's avatar
lain committed
626
  defp restrict_local(query, %{"local_only" => true}) do
lain's avatar
lain committed
627
    from(activity in query, where: activity.local == true)
lain's avatar
lain committed
628
  end
lain's avatar
lain committed
629

lain's avatar
lain committed
630
  defp restrict_local(query, _), do: query
lain's avatar
lain committed
631

632
633
  defp restrict_max(query, %{"max_id" => ""}), do: query

lain's avatar
lain committed
634
  defp restrict_max(query, %{"max_id" => max_id}) do
lain's avatar
lain committed
635
    from(activity in query, where: activity.id < ^max_id)
636
  end
lain's avatar
lain committed
637

lain's avatar
lain committed
638
  defp restrict_max(query, _), do: query
639

lain's avatar
lain committed
640
  defp restrict_actor(query, %{"actor_id" => actor_id}) do
lain's avatar
lain committed
641
    from(activity in query, where: activity.actor == ^actor_id)
642
  end
lain's avatar
lain committed
643

lain's avatar
lain committed
644
  defp restrict_actor(query, _), do: query
645

lain's avatar
lain committed
646
  defp restrict_type(query, %{"type" => type}) when is_binary(type) do
647
    from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
lain's avatar
lain committed
648
  end
lain's avatar
lain committed
649

650
  defp restrict_type(query, %{"type" => type}) do
lain's avatar
lain committed
651
    from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
652
  end
lain's avatar
lain committed
653

654
655
  defp restrict_type(query, _), do: query

656
  defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
lain's avatar
lain committed
657
658
    from(
      activity in query,
659
      where: fragment(~s(? <@ (? #> '{"object","likes"}'\)), ^ap_id, activity.data)
lain's avatar
lain committed
660
    )
661
  end
lain's avatar
lain committed
662

663
664
  defp restrict_favorited_by(query, _), do: query

eal's avatar
eal committed
665
  defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
lain's avatar
lain committed
666
667
    from(
      activity in query,
668
      where: fragment(~s(not (? #> '{"object","attachment"}' = ?\)), activity.data, ^[])
lain's avatar
lain committed
669
    )
eal's avatar
eal committed
670
  end
lain's avatar
lain committed
671

eal's avatar
eal committed
672
673
  defp restrict_media(query, _), do: query

674
675
676
677
678
679
680
681
682
  defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
    from(
      activity in query,
      where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
    )
  end

  defp restrict_replies(query, _), do: query

683
684
685
686
687
688
  defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
    from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
  end

  defp restrict_reblogs(query, _), do: query

lain's avatar
lain committed
689
690
  defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query

691
  defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
692
    mutes = info.mutes
693
694
695
696
697
698
699
700
701
702

    from(
      activity in query,
      where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
      where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
    )
  end

  defp restrict_muted(query, _), do: query

lain's avatar
lain committed
703
  defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
lain's avatar
lain committed
704
705
    blocks = info.blocks || []
    domain_blocks = info.domain_blocks || []
lain's avatar
lain committed
706
707
708

    from(
      activity in query,
709
      where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
710
      where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks),
eal's avatar
eal committed
711
      where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
712
    )
713
  end
lain's avatar
lain committed
714

715
716
  defp restrict_blocked(query, _), do: query

717
718
719
  defp restrict_unlisted(query) do
    from(
      activity in query,
lain's avatar
Format.    
lain committed
720
721
      where:
        fragment(
lain's avatar
lain committed
722
          "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
lain's avatar
Format.    
lain committed
723
724
725
          activity.data,
          ^["https://www.w3.org/ns/activitystreams#Public"]
        )
726
727
728
    )
  end

minibikini's avatar
minibikini committed
729
730
731
732
733
734
  defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
    from(activity in query, where: activity.id in ^ids)
  end

  defp restrict_pinned(query, _), do: query

Karen Konou's avatar
Karen Konou committed
735
736
737
738
739
  defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
    muted_reblogs = info.muted_reblogs || []

    from(
      activity in query,
740
741
742
743
744
745
746
      where:
        fragment(
          "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
          activity.data,
          activity.actor,
          ^muted_reblogs
        )
Karen Konou's avatar
Karen Konou committed
747
748
749
750
751
    )
  end

  defp restrict_muted_reblogs(query, _), do: query

752
753
754
755
756
757
758
  defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query

  defp maybe_preload_objects(query, _) do
    query
    |> Activity.with_preloaded_object()
  end

lain's avatar
lain committed
759
  def fetch_activities_query(recipients, opts \\ %{}) do
lain's avatar
lain committed
760
761
762
763
764
765
    base_query =
      from(
        activity in Activity,
        limit: 20,
        order_by: [fragment("? desc nulls last", activity.id)]
      )
lain's avatar
lain committed
766

lain's avatar
lain committed
767
    base_query
768
    |> maybe_preload_objects(opts)
lain's avatar
lain committed
769
    |> restrict_recipients(recipients, opts["user"])
Roger Braun's avatar
Roger Braun committed
770
    |> restrict_tag(opts)
Haelwenn's avatar
Haelwenn committed
771
772
    |> restrict_tag_reject(opts)
    |> restrict_tag_all(opts)
lain's avatar
lain committed
773
774
    |> restrict_since(opts)
    |> restrict_local(opts)
kaniini's avatar
kaniini committed
775
    |> restrict_limit(opts)
lain's avatar
lain committed
776
777
    |> restrict_max(opts)
    |> restrict_actor(opts)
778
    |> restrict_type(opts)
779
    |> restrict_favorited_by(opts)
780
    |> restrict_blocked(opts)
781
    |> restrict_muted(opts)
eal's avatar
eal committed
782
    |> restrict_media(opts)
783
    |> restrict_visibility(opts)
784
    |> restrict_replies(opts)
785
    |> restrict_reblogs(opts)
minibikini's avatar
minibikini committed
786
    |> restrict_pinned(opts)
Karen Konou's avatar
Karen Konou committed
787
    |> restrict_muted_reblogs(opts)
lain's avatar
lain committed
788
789
790
791
  end

  def fetch_activities(recipients, opts \\ %{}) do
    fetch_activities_query(recipients, opts)
lain's avatar
lain committed
792
793
    |> Repo.all()
    |> Enum.reverse()
dtluna's avatar
dtluna committed
794
795
  end

796
797
798
799
800
801
802
  def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
    fetch_activities_query([], opts)
    |> restrict_to_cc(recipients_to, recipients_cc)
    |> Repo.all()
    |> Enum.reverse()
  end

href's avatar
href committed
803
804
  def upload(file, opts \\ []) do
    with {:ok, data} <- Upload.store(file, opts) do
805
806
807
808
809
810
811
      obj_data =
        if opts[:actor] do
          Map.put(data, "actor", opts[:actor])
        else
          data
        end

812
      Repo.insert(%Object{data: obj_data})
813
    end
lain's avatar
lain committed
814
  end
lain's avatar
lain committed
815

lain's avatar
lain committed
816
  def user_data_from_user_object(data) do
lain's avatar
lain committed
817
818
819
820
821
822
823
824
825
826
827
828
829
    avatar =
      data["icon"]["url"] &&
        %{
          "type" => "Image",
          "url" => [%{"href" => data["icon"]["url"]}]
        }

    banner =
      data["image"]["url"] &&
        %{
          "type" => "Image",
          "url" => [%{"href" => data["image"]["url"]}]
        }
lain's avatar
lain committed
830

831
    locked = data["manuallyApprovesFollowers"] || false
832
833
    data = Transmogrifier.maybe_fix_user_object(data)

lain's avatar
lain committed
834
835
836
837
838
    user_data = %{
      ap_id: data["id"],
      info: %{
        "ap_enabled" => true,
        "source_data" => data,
839
840
        "banner" => banner,
        "locked" => locked
lain's avatar
lain committed
841
842
843
844
845
846
847
      },
      avatar: avatar,
      name: data["name"],
      follower_address: data["followers"],
      bio: data["summary"]
    }

848
849
850
    # nickname can be nil because of virtual actors
    user_data =
      if data["preferredUsername"] do
kaniini's avatar
kaniini committed
851
852
853
854
855
        Map.put(
          user_data,
          :nickname,
          "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
        )
856
857
858
859
      else
        Map.put(user_data, :nickname, nil)
      end

lain's avatar
lain committed
860
861
862
    {:ok, user_data}
  end

lain's avatar
lain committed
863
  def fetch_and_prepare_user_from_ap_id(ap_id) do
864
    with {:ok, data} <- fetch_and_contain_remote_object_from_id(ap_id) do
lain's avatar
lain committed
865
      user_data_from_user_object(data)
lain's avatar
lain committed
866
    else
867
      e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
lain's avatar
lain committed
868
869
870
871
    end
  end

  def make_user_from_ap_id(ap_id) do
feld's avatar
feld committed
872
    if _user = User.get_by_ap_id(ap_id) do
lain's avatar
lain committed
873
874
875
876
      Transmogrifier.upgrade_user_from_ap_id(ap_id)
    else
      with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
        User.insert_or_update_user(data)
lain's avatar
lain committed
877
      else
lain's avatar
lain committed
878
        e -> {:error, e}
lain's avatar
lain committed
879
      end
lain's avatar
lain committed
880
881
    end
  end
882

lain's avatar
lain committed
883
884
885
  def make_user_from_nickname(nickname) do
    with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
      make_user_from_ap_id(ap_id)
lain's avatar
lain committed
886
    else
feld's avatar
feld committed
887
      _e -> {:error, "No AP id in WebFinger"}
lain's avatar
lain committed
888
889
890
    end
  end

891
892
893
894
895
  def should_federate?(inbox, public) do
    if public do
      true
    else
      inbox_info = URI.parse(inbox)
href's avatar
href committed
896
      !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
897
898
899
    end
  end

900
  def publish(actor, activity) do
901
    remote_followers =
lain's avatar
lain committed
902
903
904
905
906
907
      if actor.follower_address in activity.recipients do
        {:ok, followers} = User.get_followers(actor)
        followers |> Enum.filter(&(!&1.local))
      else
        []
      end
908

909
910
    public = is_public?(activity)

lain's avatar
lain committed
911
    {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
lain's avatar
lain committed
912
    json = Jason.encode!(data)
lain's avatar
lain committed
913

914
    (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
minibikini's avatar
minibikini committed
915
916
917
918
919
920
    |> Enum.filter(fn user -> User.ap_enabled?(user) end)
    |> Enum.map(fn %{info: %{source_data: data}} ->
      (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
    end)
    |> Enum.uniq()
    |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
921
922
    |> Instances.filter_reachable()
    |> Enum.each(fn {inbox, unreachable_since} ->
minibikini's avatar
minibikini committed
923
      Federator.publish_single_ap(%{
lain's avatar
lain committed
924
925
926
        inbox: inbox,
        json: json,
        actor: actor,
927
928
        id: activity.data["id"],
        unreachable_since: unreachable_since
lain's avatar
lain committed
929
930
      })
    end)
931
  end
932

933
  def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do
lain's avatar
lain committed
934
935
    Logger.info("Federating #{id} to #{inbox}")
    host = URI.parse(inbox).host
lain's avatar
lain committed
936

937
938
    digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())

kaniini's avatar
kaniini committed
939
940
    date =
      NaiveDateTime.utc_now()
kaniini's avatar
kaniini committed
941
      |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
kaniini's avatar
kaniini committed
942

lain's avatar
lain committed
943
    signature =
944
945
946
      Pleroma.Web.HTTPSignatures.sign(actor, %{
        host: host,
        "content-length": byte_size(json),
kaniini's avatar
kaniini committed
947
948
        digest: digest,
        date: date
949
      })
lain's avatar
lain committed
950

951
    with {:ok, %{status: code}} when code in 200..299 <-
952
953
954
955
956
957
           result =
             @httpoison.post(
               inbox,
               json,
               [
                 {"Content-Type", "application/activity+json"},
kaniini's avatar
kaniini committed
958
                 {"Date", date},
959
                 {"signature", signature},
960
                 {"digest", digest}
961
962
               ]
             ) do
963
964
965
      if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
        do: Instances.set_reachable(inbox)

966
967
      result
    else
968
      {_post_result, response} ->
969
        unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
970
        {:error, response}
971
    end
lain's avatar
lain committed
972
973
  end

974
975
  # TODO:
  # This will create a Create activity, which we need internally at the moment.
976
977
978
979
  def fetch_object_from_id(id) do
    if object = Object.get_cached_by_ap_id(id) do
      {:ok, object}
    else
980
      with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
981
           nil <- Object.normalize(data),
lain's avatar
lain committed
982