summaryrefslogtreecommitdiff
path: root/gstudio/xmlrpc/pingback.py
blob: 44aaafd7770248d3c36baee0131643814070b89b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""XML-RPC methods of Gstudio Pingback"""
from urllib2 import urlopen
from urllib2 import URLError
from urllib2 import HTTPError
from urlparse import urlsplit

from django.contrib import comments
from django.utils.html import strip_tags
from django.contrib.sites.models import Site
from django.core.urlresolvers import resolve
from django.core.urlresolvers import Resolver404
from django.utils.translation import ugettext as _
from django.contrib.contenttypes.models import ContentType

from gstudio.models import Nodetype
from gstudio.settings import PINGBACK_CONTENT_LENGTH
from BeautifulSoup import BeautifulSoup
from django_xmlrpc.decorators import xmlrpc_func

UNDEFINED_ERROR = 0
SOURCE_DOES_NOT_EXIST = 16
SOURCE_DOES_NOT_LINK = 17
TARGET_DOES_NOT_EXIST = 32
TARGET_IS_NOT_PINGABLE = 33
PINGBACK_ALREADY_REGISTERED = 48


def generate_pingback_content(soup, target, max_length, trunc_char='...'):
    """Generate a description text for the pingback"""
    link = soup.find('a', href=target)

    content = strip_tags(unicode(link.findParent()))
    index = content.index(link.string)

    if len(content) > max_length:
        middle = max_length / 2
        start = index - middle
        end = index + middle

        if start <= 0:
            end -= start
            extract = content[0:end]
        else:
            extract = '%s%s' % (trunc_char, content[start:end])

        if end < len(content):
            extract += trunc_char
        return extract

    return content


@xmlrpc_func(returns='string', args=['string', 'string'])
def pingback_ping(source, target):
    """pingback.ping(sourceURI, targetURI) => 'Pingback message'

    Notifies the server that a link has been added to sourceURI,
    pointing to targetURI.

    See: http://hixie.ch/specs/pingback/pingback-1.0"""
    try:
        if source == target:
            return UNDEFINED_ERROR

        site = Site.objects.get_current()
        try:
            document = ''.join(urlopen(source).readlines())
        except (HTTPError, URLError):
            return SOURCE_DOES_NOT_EXIST

        if not target in document:
            return SOURCE_DOES_NOT_LINK

        scheme, netloc, path, query, fragment = urlsplit(target)
        if netloc != site.domain:
            return TARGET_DOES_NOT_EXIST

        try:
            view, args, kwargs = resolve(path)
        except Resolver404:
            return TARGET_DOES_NOT_EXIST

        try:
            nodetype = Nodetype.published.get(
                slug=kwargs['slug'],
                creation_date__year=kwargs['year'],
                creation_date__month=kwargs['month'],
                creation_date__day=kwargs['day'])
            if not nodetype.pingback_enabled:
                return TARGET_IS_NOT_PINGABLE
        except (KeyError, Nodetype.DoesNotExist):
            return TARGET_IS_NOT_PINGABLE

        soup = BeautifulSoup(document)
        title = soup.find('title')
        title = title and strip_tags(title) or _('No title')
        description = generate_pingback_content(soup, target,
                                                PINGBACK_CONTENT_LENGTH)

        comment, created = comments.get_model().objects.get_or_create(
            content_type=ContentType.objects.get_for_model(Nodetype),
            object_pk=nodetype.pk, user_url=source, site=site,
            defaults={'comment': description, 'user_name': title})
        if created:
            user = nodetype.authors.all()[0]
            comment.flags.create(user=user, flag='pingback')
            return 'Pingback from %s to %s registered.' % (source, target)
        return PINGBACK_ALREADY_REGISTERED
    except:
        return UNDEFINED_ERROR


@xmlrpc_func(returns='string[]', args=['string'])
def pingback_extensions_get_pingbacks(target):
    """pingback.extensions.getPingbacks(url) => '[url, url, ...]'

    Returns an array of URLs that link to the specified url.

    See: http://www.aquarionics.com/misc/archives/blogite/0198.html"""
    site = Site.objects.get_current()

    scheme, netloc, path, query, fragment = urlsplit(target)
    if netloc != site.domain:
        return TARGET_DOES_NOT_EXIST

    try:
        view, args, kwargs = resolve(path)
    except Resolver404:
        return TARGET_DOES_NOT_EXIST

    try:
        nodetype = Nodetype.published.get(
            slug=kwargs['slug'],
            creation_date__year=kwargs['year'],
            creation_date__month=kwargs['month'],
            creation_date__day=kwargs['day'])
    except (KeyError, Nodetype.DoesNotExist):
        return TARGET_IS_NOT_PINGABLE

    return [pingback.user_url for pingback in nodetype.pingbacks]