Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Pleroma
pleroma
Commits
ab0114fb
Commit
ab0114fb
authored
Apr 24, 2017
by
lain
Browse files
Return salmon path for users, basic incoming salmon handling.
parent
34d3aea9
Changes
9
Hide whitespace changes
Inline
Side-by-side
lib/pleroma/web/activity_pub/activity_pub.ex
View file @
ab0114fb
...
...
@@ -19,6 +19,48 @@ def insert(map) when is_map(map) 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.
...
...
lib/pleroma/web/ostatus/feed_representer.ex
View file @
ab0114fb
...
...
@@ -23,6 +23,7 @@ def to_simple_form(user, activities, users) do
{
: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
...
...
lib/pleroma/web/ostatus/ostatus.ex
View file @
ab0114fb
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,132 @@ def pubsub_path(user) 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
# Parse mention
# wire up replies
# Set correct context
# Set correct statusnet ids.
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
=
ActivityPub
.
generate_context_id
to
=
[
"https://www.w3.org/ns/activitystreams#Public"
]
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
(
author
,
doc
)
do
query
=
from
user
in
User
,
where:
user
.
local
==
false
and
fragment
(
"? @> ?"
,
user
.
info
,
^
%{
ostatus_uri:
author
})
user
=
Repo
.
one
(
query
)
if
is_nil
(
user
)
do
make_user
(
doc
)
else
{
:ok
,
user
}
end
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
lib/pleroma/web/ostatus/ostatus_controller.ex
View file @
ab0114fb
...
...
@@ -25,7 +25,14 @@ def feed(conn, %{"nickname" => nickname}) 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
lib/pleroma/web/router.ex
View file @
ab0114fb
...
...
@@ -74,6 +74,7 @@ def user_fetcher(username) 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
...
...
lib/pleroma/web/twitter_api/twitter_api.ex
View file @
ab0114fb
...
...
@@ -28,11 +28,33 @@ def create_status(user = %User{}, data = %{}) do
date
=
make_date
()
activity
=
%{
"type"
=>
"Create"
,
"to"
=>
to
,
"actor"
=>
user
.
ap_id
,
"object"
=>
%{
# Wire up reply info.
[
to
,
context
,
object
,
additional
]
=
with
inReplyToId
when
not
is_nil
(
inReplyToId
)
<-
data
[
"in_reply_to_status_id"
],
inReplyTo
<-
Repo
.
get
(
Activity
,
inReplyToId
),
context
<-
inReplyTo
.
data
[
"context"
]
do
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"
=>
inReplyToId
,
"statusnetConversationId"
=>
inReplyTo
.
data
[
"statusnetConversationId"
]
}
additional
=
%{
"statusnetConversationId"
=>
inReplyTo
.
data
[
"statusnetConversationId"
]
}
[
to
,
context
,
object
,
additional
]
else
_e
->
object
=
%{
"type"
=>
"Note"
,
"to"
=>
to
,
"content"
=>
content_html
,
...
...
@@ -40,36 +62,11 @@ def create_status(user = %User{}, data = %{}) 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
...
...
lib/pleroma/web/web_finger/web_finger.ex
View file @
ab0114fb
...
...
@@ -31,7 +31,8 @@ def represent_user(user) 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
...
...
test/web/ostatus/feed_representer_test.exs
View file @
ab0114fb
...
...
@@ -27,6 +27,7 @@ test "returns a feed of the last 20 items of the user" do
<title>#{user.nickname}'s timeline</title>
<updated>#{most_recent_update}</updated>
<link rel="hub" href="#{OStatus.pubsub_path(user)}" />
<link rel="salmon" href="#{OStatus.salmon_path(user)}" />
<link rel="self" href="#{OStatus.feed_path(user)}" />
<author>
#{user_xml}
...
...
test/web/ostatus/ostatus_test.exs
0 → 100644
View file @
ab0114fb
defmodule
Pleroma
.
Web
.
OStatusTest
do
use
Pleroma
.
DataCase
alias
Pleroma
.
Web
.
OStatus
test
"handle incoming notes"
do
incoming
=
File
.
read!
(
"test/fixtures/incoming_note_activity.xml"
)
{
:ok
,
activity
}
=
OStatus
.
handle_incoming
(
incoming
)
assert
activity
.
data
[
"type"
]
==
"Create"
assert
activity
.
data
[
"object"
][
"type"
]
==
"Note"
assert
activity
.
data
[
"published"
]
==
"2017-04-23T14:51:03+00:00"
end
describe
"new remote user creation"
do
test
"make new user or find them based on an 'author' xml doc"
do
incoming
=
File
.
read!
(
"test/fixtures/user_name_only.xml"
)
{
doc
,
_rest
}
=
:xmerl_scan
.
string
(
to_charlist
(
incoming
))
{
:ok
,
user
}
=
OStatus
.
find_or_make_user
(
doc
)
assert
user
.
name
==
"lambda"
assert
user
.
nickname
==
"lambda"
assert
user
.
local
==
false
assert
user
.
info
[
"ostatus_uri"
]
==
"http://gs.example.org:4040/index.php/user/1"
assert
user
.
info
[
"system"
]
==
"ostatus"
assert
user
.
ap_id
==
"http://gs.example.org:4040/index.php/user/1"
{
:ok
,
user_again
}
=
OStatus
.
find_or_make_user
(
doc
)
assert
user
==
user_again
end
test
"tries to use the information in poco fields"
do
incoming
=
File
.
read!
(
"test/fixtures/user_full.xml"
)
{
doc
,
_rest
}
=
:xmerl_scan
.
string
(
to_charlist
(
incoming
))
{
:ok
,
user
}
=
OStatus
.
find_or_make_user
(
doc
)
assert
user
.
name
==
"Constance Variable"
assert
user
.
nickname
==
"lambadalambda"
assert
user
.
local
==
false
assert
user
.
info
[
"ostatus_uri"
]
==
"http://gs.example.org:4040/index.php/user/1"
assert
user
.
info
[
"system"
]
==
"ostatus"
assert
user
.
ap_id
==
"http://gs.example.org:4040/index.php/user/1"
assert
List
.
first
(
user
.
avatar
[
"url"
])[
"href"
]
==
"http://gs.example.org:4040/theme/neo-gnu/default-avatar-profile.png"
{
:ok
,
user_again
}
=
OStatus
.
find_or_make_user
(
doc
)
assert
user
==
user_again
end
end
end
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment