zinnia.ping
Covered: 80 lines
Missed: 53 lines
Skipped 28 lines
Percent: 60 %
  1
"""Pings utilities for Zinnia"""
  2
import socket
  3
import xmlrpclib
  4
import threading
  5
from urllib2 import urlopen
  6
from urlparse import urlsplit
  7
from logging import getLogger
  9
from BeautifulSoup import BeautifulSoup
 11
from django.contrib.sites.models import Site
 12
from django.core.urlresolvers import reverse
 14
from zinnia.settings import PROTOCOL
 17
class URLRessources(object):
 18
    """Object defining the ressources of the website"""
 20
    def __init__(self):
 21
        self.current_site = Site.objects.get_current()
 22
        self.site_url = '%s://%s' % (PROTOCOL, self.current_site.domain)
 23
        self.blog_url = '%s%s' % (self.site_url,
 24
                                  reverse('zinnia_entry_archive_index'))
 25
        self.blog_feed = '%s%s' % (self.site_url,
 26
                                   reverse('zinnia_entry_latest_feed'))
 29
class DirectoryPinger(threading.Thread):
 30
    """Threaded Directory Pinger"""
 32
    def __init__(self, server_name, entries, timeout=10, start_now=True):
 33
        self.results = []
 34
        self.timeout = timeout
 35
        self.entries = entries
 36
        self.server_name = server_name
 37
        self.server = xmlrpclib.ServerProxy(self.server_name)
 38
        self.ressources = URLRessources()
 40
        threading.Thread.__init__(self)
 41
        if start_now:
 42
            self.start()
 44
    def run(self):
 45
        """Ping entries to a Directory in a Thread"""
 46
        logger = getLogger('zinnia.ping.directory')
 47
        socket.setdefaulttimeout(self.timeout)
 48
        for entry in self.entries:
 49
            reply = self.ping_entry(entry)
 50
            self.results.append(reply)
 51
            logger.info('%s : %s' % (self.server_name, reply['message']))
 52
        socket.setdefaulttimeout(None)
 54
    def ping_entry(self, entry):
 55
        """Ping an entry to a Directory"""
 56
        entry_url = '%s%s' % (self.ressources.site_url,
 57
                              entry.get_absolute_url())
 58
        categories = '|'.join([c.title for c in entry.categories.all()])
 60
        try:
 61
            reply = self.server.weblogUpdates.extendedPing(
 62
                self.ressources.current_site.name,
 63
                self.ressources.blog_url, entry_url,
 64
                self.ressources.blog_feed, categories)
 65
        except Exception:
 66
            try:
 67
                reply = self.server.weblogUpdates.ping(
 68
                    self.ressources.current_site.name,
 69
                    self.ressources.blog_url, entry_url,
 70
                    categories)
 71
            except Exception:
 72
                reply = {'message': '%s is an invalid directory.' % \
 73
                         self.server_name,
 74
                         'flerror': True}
 75
        return reply
 78
class ExternalUrlsPinger(threading.Thread):
 79
    """Threaded ExternalUrls Pinger"""
 81
    def __init__(self, entry, timeout=10, start_now=True):
 82
        self.results = []
 83
        self.entry = entry
 84
        self.timeout = timeout
 85
        self.ressources = URLRessources()
 86
        self.entry_url = '%s%s' % (self.ressources.site_url,
 87
                                   self.entry.get_absolute_url())
 89
        threading.Thread.__init__(self)
 90
        if start_now:
 91
            self.start()
 93
    def run(self):
 94
        """Ping external URLS in a Thread"""
 95
        logger = getLogger('zinnia.ping.external_urls')
 96
        socket.setdefaulttimeout(self.timeout)
 98
        external_urls = self.find_external_urls(self.entry)
 99
        external_urls_pingable = self.find_pingback_urls(external_urls)
101
        for url, server_name in external_urls_pingable.items():
102
            reply = self.pingback_url(server_name, url)
103
            self.results.append(reply)
104
            logger.info('%s : %s' % (url, reply))
106
        socket.setdefaulttimeout(None)
108
    def is_external_url(self, url, site_url):
109
        """Check of the url in an external url"""
110
        url_splitted = urlsplit(url)
111
        if not url_splitted.netloc:
112
            return False
113
        return url_splitted.netloc != urlsplit(site_url).netloc
115
    def find_external_urls(self, entry):
116
        """Find external urls in an entry"""
117
        soup = BeautifulSoup(entry.html_content)
118
        external_urls = [a['href'] for a in soup.findAll('a')
119
                         if self.is_external_url(
120
                             a['href'], self.ressources.site_url)]
121
        return external_urls
123
    def find_pingback_href(self, content):
124
        """Try to find Link markup to pingback url"""
125
        soup = BeautifulSoup(content)
126
        for link in soup.findAll('link'):
127
            dict_attr = dict(link.attrs)
128
            if 'rel' in dict_attr and 'href' in dict_attr:
129
                if dict_attr['rel'].lower() == 'pingback':
130
                    return dict_attr.get('href')
132
    def find_pingback_urls(self, urls):
133
        """Find the pingback urls of each urls"""
134
        pingback_urls = {}
136
        for url in urls:
137
            try:
138
                page = urlopen(url)
139
                server_url = page.info().get('X-Pingback') or \
140
                             self.find_pingback_href(page.read())
141
                if server_url:
142
                    server_url_splitted = urlsplit(server_url)
143
                    if not server_url_splitted.netloc:
144
                        url_splitted = urlsplit(url)
145
                        server_url = '%s://%s%s' % (url_splitted.scheme,
146
                                                    url_splitted.netloc,
147
                                                    server_url)
148
                    pingback_urls[url] = server_url
149
            except IOError:
150
                pass
151
        return pingback_urls
153
    def pingback_url(self, server_name, target_url):
154
        """Do a pingback call for the target url"""
155
        try:
156
            server = xmlrpclib.ServerProxy(server_name)
157
            reply = server.pingback.ping(self.entry_url, target_url)
158
        except xmlrpclib.Error:
159
            reply = '%s cannot be pinged.' % target_url
160
        return reply