aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/user.ex2
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex42
-rw-r--r--lib/pleroma/web/ostatus/activity_representer.ex4
-rw-r--r--lib/pleroma/web/ostatus/feed_representer.ex4
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex128
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex11
-rw-r--r--lib/pleroma/web/router.ex1
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex125
-rw-r--r--lib/pleroma/web/web_finger/web_finger.ex3
9 files changed, 246 insertions, 74 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 5e579dc44..9b7912c5b 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -15,6 +15,8 @@ defmodule Pleroma.User do
field :following, { :array, :string }, default: []
field :ap_id, :string
field :avatar, :map
+ field :local, :boolean, default: true
+ field :info, :map
timestamps()
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index e9f0dcd32..7264123d8 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -19,6 +19,48 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Repo.insert(%Activity{data: map})
end
+ def create(to, actor, context, object, additional \\ %{}, published \\ nil) do
+ published = published || make_date()
+
+ activity = %{
+ "type" => "Create",
+ "to" => to,
+ "actor" => actor.ap_id,
+ "object" => object,
+ "published" => published,
+ "context" => context
+ }
+ |> Map.merge(additional)
+
+ with {:ok, activity} <- insert(activity) do
+ {:ok, activity} = add_conversation_id(activity)
+
+ if actor.local do
+ Pleroma.Web.Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
+ end
+
+ {:ok, activity}
+ end
+ end
+
+ defp add_conversation_id(activity) do
+ if is_integer(activity.data["statusnetConversationId"]) do
+ {:ok, activity}
+ else
+ data = activity.data
+ |> put_in(["object", "statusnetConversationId"], activity.id)
+ |> put_in(["statusnetConversationId"], activity.id)
+
+ object = Object.get_by_ap_id(activity.data["object"]["id"])
+
+ changeset = Ecto.Changeset.change(object, data: data["object"])
+ Repo.update(changeset)
+
+ changeset = Ecto.Changeset.change(activity, data: data)
+ Repo.update(changeset)
+ end
+ end
+
def like(%User{ap_id: ap_id} = user, %Object{data: %{ "id" => id}} = object) do
cond do
# There's already a like here, so return the original activity.
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
index 590abc8bb..367212fe1 100644
--- a/lib/pleroma/web/ostatus/activity_representer.ex
+++ b/lib/pleroma/web/ostatus/activity_representer.ex
@@ -19,7 +19,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
{:title, ['New note by #{user.nickname}']},
{:content, [type: 'html'], h.(activity.data["object"]["content"])},
{:published, h.(inserted_at)},
- {:updated, h.(updated_at)}
+ {:updated, h.(updated_at)},
+ {:"ostatus:conversation", [], h.(activity.data["context"])},
+ {:link, [href: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}
] ++ attachments
end
diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex
index 14ac3ebf4..10a1ffb25 100644
--- a/lib/pleroma/web/ostatus/feed_representer.ex
+++ b/lib/pleroma/web/ostatus/feed_representer.ex
@@ -17,12 +17,14 @@ defmodule Pleroma.Web.OStatus.FeedRepresenter do
:feed, [
xmlns: 'http://www.w3.org/2005/Atom',
"xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
- "xmlns:poco": 'http://portablecontacts.net/spec/1.0'
+ "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
+ "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
], [
{:id, h.(OStatus.feed_path(user))},
{:title, ['#{user.nickname}\'s timeline']},
{:updated, h.(most_recent_update)},
{:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []},
+ {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []},
{:link, [rel: 'self', href: h.(OStatus.feed_path(user))], []},
{:author, UserRepresenter.to_simple_form(user)},
] ++ entries
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index d21b9078f..65141f826 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -1,5 +1,9 @@
defmodule Pleroma.Web.OStatus do
- alias Pleroma.Web
+ import Ecto.Query
+ require Logger
+
+ alias Pleroma.{Repo, User, Web}
+ alias Pleroma.Web.ActivityPub.ActivityPub
def feed_path(user) do
"#{user.ap_id}/feed.atom"
@@ -9,6 +13,126 @@ defmodule Pleroma.Web.OStatus do
"#{Web.base_url}/push/hub/#{user.nickname}"
end
- def user_path(user) do
+ def salmon_path(user) do
+ "#{user.ap_id}/salmon"
+ end
+
+ def handle_incoming(xml_string) do
+ {doc, _rest} = :xmerl_scan.string(to_charlist(xml_string))
+
+ {:xmlObj, :string, object_type } = :xmerl_xpath.string('string(/entry/activity:object-type[1])', doc)
+
+ case object_type do
+ 'http://activitystrea.ms/schema/1.0/note' ->
+ handle_note(doc)
+ _ ->
+ Logger.error("Couldn't parse incoming document")
+ end
+ end
+
+ # TODO
+ # wire up replies
+ def handle_note(doc) do
+ content_html = string_from_xpath("/entry/content[1]", doc)
+
+ [author] = :xmerl_xpath.string('/entry/author[1]', doc)
+ {:ok, actor} = find_or_make_user(author)
+
+ context = string_from_xpath("/entry/ostatus:conversation[1]", doc) |> String.trim
+ context = if String.length(context) > 0 do
+ context
+ else
+ ActivityPub.generate_context_id
+ end
+
+ to = [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ]
+
+ mentions = :xmerl_xpath.string('/entry/link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]', doc)
+ |> Enum.map(fn(person) -> string_from_xpath("@href", person) end)
+
+ to = to ++ mentions
+
+ date = string_from_xpath("/entry/published", doc)
+
+ object = %{
+ "type" => "Note",
+ "to" => to,
+ "content" => content_html,
+ "published" => date,
+ "context" => context,
+ "actor" => actor.ap_id
+ }
+
+ ActivityPub.create(to, actor, context, object, %{}, date)
+ end
+
+ def find_or_make_user(author_doc) do
+ {:xmlObj, :string, uri } = :xmerl_xpath.string('string(/author[1]/uri)', author_doc)
+
+ query = from user in User,
+ where: user.local == false and fragment("? @> ?", user.info, ^%{ostatus_uri: to_string(uri)})
+
+ user = Repo.one(query)
+
+ if is_nil(user) do
+ make_user(author_doc)
+ else
+ {:ok, user}
+ end
+ end
+
+ defp string_from_xpath(xpath, doc) do
+ {:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc)
+
+ res = res
+ |> to_string
+ |> String.trim
+
+ if res == "", do: nil, else: res
+ end
+
+ def make_user(author_doc) do
+ author = string_from_xpath("/author[1]/uri", author_doc)
+ name = string_from_xpath("/author[1]/name", author_doc)
+ preferredUsername = string_from_xpath("/author[1]/poco:preferredUsername", author_doc)
+ displayName = string_from_xpath("/author[1]/poco:displayName", author_doc)
+ avatar = make_avatar_object(author_doc)
+
+ data = %{
+ local: false,
+ name: preferredUsername || name,
+ nickname: displayName || name,
+ ap_id: author,
+ info: %{
+ "ostatus_uri" => author,
+ "host" => URI.parse(author).host,
+ "system" => "ostatus"
+ },
+ avatar: avatar
+ }
+
+ Repo.insert(Ecto.Changeset.change(%User{}, data))
+ end
+
+ # TODO: Just takes the first one for now.
+ defp make_avatar_object(author_doc) do
+ href = string_from_xpath("/author[1]/link[@rel=\"avatar\"]/@href", author_doc)
+ type = string_from_xpath("/author[1]/link[@rel=\"avatar\"]/@type", author_doc)
+
+ if href do
+ %{
+ "type" => "Image",
+ "url" =>
+ [%{
+ "type" => "Link",
+ "mediaType" => type,
+ "href" => href
+ }]
+ }
+ else
+ nil
+ end
end
end
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 3c8d8c0f1..4174db786 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -25,7 +25,14 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|> send_resp(200, response)
end
- def temp(conn, params) do
- IO.inspect(params)
+ def salmon_incoming(conn, params) do
+ {:ok, body, _conn} = read_body(conn)
+ magic_key = Pleroma.Web.Salmon.fetch_magic_key(body)
+ {:ok, doc} = Pleroma.Web.Salmon.decode_and_validate(magic_key, body)
+
+ Pleroma.Web.OStatus.handle_incoming(doc)
+
+ conn
+ |> send_resp(200, "")
end
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index a4f13c879..c98eac688 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -74,6 +74,7 @@ defmodule Pleroma.Web.Router do
pipe_through :ostatus
get "/users/:nickname/feed", OStatus.OStatusController, :feed
+ post "/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming
post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index f2b2b4418..1c3396d27 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -5,34 +5,68 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
import Ecto.Query
- def create_status(user = %User{}, data = %{}) do
- attachments = Enum.map(data["media_ids"] || [], fn (media_id) ->
- Repo.get(Object, media_id).data
- end)
-
- context = ActivityPub.generate_context_id
-
- content = HtmlSanitizeEx.strip_tags(data["status"])
- |> String.replace("\n", "<br>")
-
- mentions = parse_mentions(content)
-
+ def to_for_user_and_mentions(user, mentions) do
default_to = [
User.ap_followers(user),
"https://www.w3.org/ns/activitystreams#Public"
]
- to = default_to ++ Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end)
+ default_to ++ Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end)
+ end
+
+ def format_input(text, mentions) do
+ HtmlSanitizeEx.strip_tags(text)
+ |> String.replace("\n", "<br>")
+ |> add_user_links(mentions)
+ end
+
+ def attachments_from_ids(ids) do
+ Enum.map(ids || [], fn (media_id) ->
+ Repo.get(Object, media_id).data
+ end)
+ end
+
+ def get_replied_to_activity(id) when not is_nil(id) do
+ Repo.get(Activity, id)
+ end
- content_html = add_user_links(content, mentions)
+ def get_replied_to_activity(_), do: nil
+ def create_status(user = %User{}, data = %{"status" => status}) do
+ attachments = attachments_from_ids(data["media_ids"])
+ context = ActivityPub.generate_context_id
+ mentions = parse_mentions(status)
+ content_html = format_input(status, mentions)
+ to = to_for_user_and_mentions(user, mentions)
date = make_date()
- activity = %{
- "type" => "Create",
- "to" => to,
- "actor" => user.ap_id,
- "object" => %{
+ inReplyTo = get_replied_to_activity(data["in_reply_to_status_id"])
+
+ # Wire up reply info.
+ [to, context, object, additional] =
+ if inReplyTo do
+ context = inReplyTo.data["context"]
+ to = to ++ [inReplyTo.data["actor"]]
+
+ object = %{
+ "type" => "Note",
+ "to" => to,
+ "content" => content_html,
+ "published" => date,
+ "context" => context,
+ "attachment" => attachments,
+ "actor" => user.ap_id,
+ "inReplyTo" => inReplyTo.data["object"]["id"],
+ "inReplyToStatusId" => inReplyTo.id,
+ "statusnetConversationId" => inReplyTo.data["statusnetConversationId"]
+ }
+ additional = %{
+ "statusnetConversationId" => inReplyTo.data["statusnetConversationId"]
+ }
+
+ [to, context, object, additional]
+ else
+ object = %{
"type" => "Note",
"to" => to,
"content" => content_html,
@@ -40,36 +74,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
"context" => context,
"attachment" => attachments,
"actor" => user.ap_id
- },
- "published" => date,
- "context" => context
- }
-
- # Wire up reply info.
- activity = with inReplyToId when not is_nil(inReplyToId) <- data["in_reply_to_status_id"],
- inReplyTo <- Repo.get(Activity, inReplyToId),
- context <- inReplyTo.data["context"]
- do
-
- to = activity["to"] ++ [inReplyTo.data["actor"]]
-
- activity
- |> put_in(["to"], to)
- |> put_in(["context"], context)
- |> put_in(["object", "context"], context)
- |> put_in(["object", "inReplyTo"], inReplyTo.data["object"]["id"])
- |> put_in(["object", "inReplyToStatusId"], inReplyToId)
- |> put_in(["statusnetConversationId"], inReplyTo.data["statusnetConversationId"])
- |> put_in(["object", "statusnetConversationId"], inReplyTo.data["statusnetConversationId"])
- else _e ->
- activity
- end
-
- with {:ok, activity} <- ActivityPub.insert(activity) do
- {:ok, activity} = add_conversation_id(activity)
- Pleroma.Web.Websub.publish(Pleroma.Web.OStatus.feed_path(user), user, activity)
- {:ok, activity}
+ }
+ [to, context, object, %{}]
end
+
+ ActivityPub.create(to, user, context, object, additional, data)
end
def fetch_friend_statuses(user, opts \\ %{}) do
@@ -226,24 +235,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
Enum.reduce(mentions, text, fn ({match, %User{ap_id: ap_id}}, text) -> String.replace(text, match, "<a href='#{ap_id}'>#{match}</a>") end)
end
- defp add_conversation_id(activity) do
- if is_integer(activity.data["statusnetConversationId"]) do
- {:ok, activity}
- else
- data = activity.data
- |> put_in(["object", "statusnetConversationId"], activity.id)
- |> put_in(["statusnetConversationId"], activity.id)
-
- object = Object.get_by_ap_id(activity.data["object"]["id"])
-
- changeset = Ecto.Changeset.change(object, data: data["object"])
- Repo.update(changeset)
-
- changeset = Ecto.Changeset.change(activity, data: data)
- Repo.update(changeset)
- end
- end
-
def register_user(params) do
params = %{
nickname: params["nickname"],
@@ -262,7 +253,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
{:error, changeset} ->
errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|> Poison.encode!
- {:error, %{error: errors}}
+ {:error, %{error: errors}}
end
end
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index eb540e92a..18459e8f0 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -31,7 +31,8 @@ defmodule Pleroma.Web.WebFinger do
[
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.host}"},
{:Alias, user.ap_id},
- {:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}}
+ {:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}},
+ {:Link, %{rel: "salmon", href: OStatus.salmon_path(user)}}
]
}
|> XmlBuilder.to_doc