Exemple #1
0
    def apply_sync_actions(self, device, sync_actions):
        """ Applies the sync-actions to the device """

        add, rem = sync_actions

        podcasts = get_to_dict(Podcast, add + rem, get_id=Podcast.get_id)

        for podcast_id in add:
            podcast = podcasts.get(podcast_id, None)
            if podcast is None:
                continue
            try:
                podcast.subscribe(self, device)
            except SubscriptionException as e:
                log('Web: %(username)s: cannot sync device: %(error)s' %
                    dict(username=self.username, error=repr(e)))

        for podcast_id in rem:
            podcast = podcasts.get(podcast_id, None)
            if not podcast:
                continue

            try:
                podcast.unsubscribe(self, device)
            except SubscriptionException as e:
                log('Web: %(username)s: cannot sync device: %(error)s' %
                    dict(username=self.username, error=repr(e)))
Exemple #2
0
def set_subscriptions(urls, user, device_uid, user_agent):

    device = get_device(user, device_uid, user_agent, undelete=True)

    subscriptions = dict( (p.url, p) for p in device.get_subscribed_podcasts())
    new = [p for p in urls if p not in subscriptions.keys()]
    rem = [p for p in subscriptions.keys() if p not in urls]

    subscriber = BulkSubscribe(user, device, podcasts=subscriptions)

    for r in rem:
        subscriber.add_action(r, 'unsubscribe')

    for n in new:
        subscriber.add_action(n, 'subscribe')

    try:
        errors = subscriber.execute()
    except BulkException as be:
        for err in be.errors:
            log('Simple API: %(username)s: Updating subscription for '
                    '%(podcast_url)s on %(device_uid)s failed: '
                    '%(error)s (%(reason)s)'.format(username=user.username,
                        podcast_url=err.doc, device_uid=device.uid,
                        error=err.error, reason=err.reason)
                )

    # Only an empty response is a successful response
    return HttpResponse('', mimetype='text/plain')
Exemple #3
0
    def wrapper(request, username, *args, **kwargs):

        if request.user.username.lower() == username.lower():
            return protected_view(request, *args, username=username, **kwargs)

        else:
            log('username in authentication (%s) and in requested resource (%s) don\'t match' % (request.user.username, username))
            return HttpResponseBadRequest('username in authentication (%s) and in requested resource (%s) don\'t match' % (request.user.username, username))
Exemple #4
0
def maintenance(dry_run=False):
    """
    This currently checks how many podcasts could be removed by
    applying both basic sanitizing rules and those from the database.

    This will later be used to replace podcasts!
    """

    podcast_rules = get_sanitizing_rules("podcast")
    episode_rules = get_sanitizing_rules("episode")

    num_podcasts = models.Podcast.count()

    print "Stats"
    print " * %d podcasts - %d rules" % (num_podcasts, len(podcast_rules))
    if dry_run:
        print " * dry run - nothing will be written to the database"
    print

    print "precompiling regular expressions"

    podcast_rules = list(precompile_rules(podcast_rules))
    episode_rules = list(precompile_rules(episode_rules))

    p_stats = collections.defaultdict(int)
    e_stats = collections.defaultdict(int)

    podcasts = Podcast.objects.only("id", "url").order_by("id").iterator()

    for n, p in enumerate(podcasts):
        try:
            su = sanitize_url(p.url, rules=podcast_rules)
        except Exception, e:
            log("failed to sanitize url for podcast %s: %s" % (p.id, e))
            print "failed to sanitize url for podcast %s: %s" % (p.id, e)
            p_stats["error"] += 1
            continue

        # nothing to do
        if su == p.url:
            p_stats["unchanged"] += 1
            continue

        # invalid podcast, remove
        if su == "":
            try:
                if not dry_run:
                    p.delete()
                p_stats["deleted"] += 1

            except Exception, e:
                log("failed to delete podcast %s: %s" % (p.id, e))
                print "failed to delete podcast %s: %s" % (p.id, e)
                p_stats["error"] += 1

            continue
Exemple #5
0
def episodes(request, username, version=1):

    version = int(version)
    now = datetime.now()
    now_ = get_timestamp(now)
    ua_string = request.META.get('HTTP_USER_AGENT', '')

    if request.method == 'POST':
        try:
            actions = json.loads(request.raw_post_data)
        except (JSONDecodeError, UnicodeDecodeError) as e:
            log('Advanced API: could not decode episode update POST data for user %s: %s' % (username, e))
            return HttpResponseBadRequest()

        try:
            update_urls = update_episodes(request.user, actions, now, ua_string)
        except DeviceUIDException as e:
            import traceback
            log('could not update episodes for user %s: %s %s: %s' % (username, e, traceback.format_exc(), actions))
            return HttpResponseBadRequest(str(e))

        return JsonResponse({'timestamp': now_, 'update_urls': update_urls})

    elif request.method == 'GET':
        podcast_url= request.GET.get('podcast', None)
        device_uid = request.GET.get('device', None)
        since_     = request.GET.get('since', None)
        aggregated = parse_bool(request.GET.get('aggregated', False))

        try:
            since = datetime.fromtimestamp(float(since_)) if since_ else None
        except ValueError:
            return HttpResponseBadRequest('since-value is not a valid timestamp')

        if podcast_url:
            podcast = Podcast.for_url(podcast_url)
            if not podcast:
                raise Http404
        else:
            podcast = None

        if device_uid:

            try:
                device = request.user.get_device_by_uid(device_uid)
            except DeviceDoesNotExist as e:
                return HttpResponseNotFound(str(e))

        else:
            device = None

        changes = get_episode_changes(request.user, podcast, device, since,
                now, aggregated, version)

        return JsonResponse(changes)
Exemple #6
0
    def get_target(self):
        """
        returns the target (device or device group) that has been selected
        in the form.
        """
        if not self.is_valid():
            log('no target given in SyncForm')
            raise ValueError(_('No device selected'))

        target = self.cleaned_data['targets']
        return target
Exemple #7
0
def view_or_basicauth(view, request, username, token_name, realm = "", *args, **kwargs):

    user = User.get_user(username)
    if not user:
        raise Http404

    token = getattr(user, token_name, '')

    # check if a token is required at all
    if token == '':
        return view(request, username, *args, **kwargs)

    # this header format is used when passing auth-headers
    # from Aapache to fcgi
    if 'AUTHORIZATION' in request.META:
        auth = request.META['AUTHORIZATION']

    elif 'HTTP_AUTHORIZATION' in request.META:
        auth = request.META['HTTP_AUTHORIZATION']

    else:
        return auth_request()


    auth = auth.split(None, 1)

    if len(auth) == 2:
        auth_type, credentials = auth

        # NOTE: We are only support basic authentication for now.
        if auth_type.lower() == 'basic':
            credentials = credentials.decode('base64').split(':', 1)
            if len(credentials) == 2:

                uname, passwd = credentials

                if uname != username:
                    return auth_request()

                if token == passwd:
                    try:
                        return view(request, uname, *args, **kwargs)
                    except Exception, e:
                        log(repr(e))
                        return HttpResponseBadRequest(e)
Exemple #8
0
def upload(request):
    try:
        emailaddr = request.POST['username']
        password  = request.POST['password']
        action    = request.POST['action']
        protocol  = request.POST['protocol']
        opml      = request.FILES['opml'].read()
    except MultiValueDictKeyError:
        return HttpResponse("@PROTOERROR", mimetype='text/plain')

    user = auth(emailaddr, password)
    if (not user):
        return HttpResponse('@AUTHFAIL', mimetype='text/plain')

    dev = get_device(user, LEGACY_DEVICE_UID,
            request.META.get('HTTP_USER_AGENT', ''))

    existing_urls = [x.url for x in dev.get_subscribed_podcasts()]

    i = Importer(opml)

    podcast_urls = [p['url'] for p in i.items]
    podcast_urls = sanitize_urls(podcast_urls)
    podcast_urls = filter(lambda x: x, podcast_urls)

    new = [u for u in podcast_urls if u not in existing_urls]
    rem = [u for e in existing_urls if u not in podcast_urls]

    #remove duplicates
    new = list(set(new))
    rem = list(set(rem))

    for n in new:
        try:
            p = Podcast.for_url(n, create=True)
        except IntegrityError, e:
            log('/upload: Error trying to get podcast object: %s (error: %s)' % (n, e))
            continue

        try:
            p.subscribe(user, dev)
        except Exception as e:
            log('Legacy API: %(username)s: could not subscribe to podcast %(podcast_url)s on device %(device_id)s: %(exception)s' %
                {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id, 'exception': e})
Exemple #9
0
def unsubscribe(request, podcast, device_uid):

    return_to = request.GET.get('return_to', None)

    if not return_to:
        raise Http404('Wrong URL')

    try:
        device = request.user.get_device_by_uid(device_uid)

    except DeviceDoesNotExist as e:
        messages.error(request, str(e))

    try:
        podcast.unsubscribe(request.user, device)
    except Exception as e:
        log('Web: %(username)s: could not unsubscribe from podcast %(podcast_url)s on device %(device_id)s: %(exception)s' %
            {'username': request.user.username, 'podcast_url': podcast.url, 'device_id': device.id, 'exception': e})

    return HttpResponseRedirect(return_to)
Exemple #10
0
def update_subscriptions(user, device, add, remove):

    for a in add:
        if a in remove:
            raise IntegrityError('can not add and remove %s at the same time' % a)

    add_s = list(sanitize_urls(add, 'podcast'))
    rem_s = list(sanitize_urls(remove, 'podcast'))

    assert len(add) == len(add_s) and len(remove) == len(rem_s)

    updated_urls = filter(lambda (a, b): a != b, zip(add + remove, add_s + rem_s))

    add_s = filter(None, add_s)
    rem_s = filter(None, rem_s)

    # If two different URLs (in add and remove) have
    # been sanitized to the same, we ignore the removal
    rem_s = filter(lambda x: x not in add_s, rem_s)

    subscriber = BulkSubscribe(user, device)

    for a in add_s:
        subscriber.add_action(a, 'subscribe')

    for r in rem_s:
        subscriber.add_action(r, 'unsubscribe')

    try:
        subscriber.execute()
    except BulkException as be:
        for err in be.errors:
            log('Advanced API: %(username)s: Updating subscription for '
                    '%(podcast_url)s on %(device_uid)s failed: '
                    '%(rerror)s (%(reason)s)'.format(username=user.username,
                        podcast_url=err.doc, device_uid=device.uid,
                        error=err.error, reason=err.reason)
                )

    return updated_urls
Exemple #11
0
        except IntegrityError, e:
            log('/upload: Error trying to get podcast object: %s (error: %s)' % (n, e))
            continue

        try:
            p.subscribe(user, dev)
        except Exception as e:
            log('Legacy API: %(username)s: could not subscribe to podcast %(podcast_url)s on device %(device_id)s: %(exception)s' %
                {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id, 'exception': e})

    for r in rem:
        p = Podcast.for_url(r, create=True)
        try:
            p.unsubscribe(user, dev)
        except Exception as e:
            log('Legacy API: %(username): could not unsubscribe from podcast %(podcast_url) on device %(device_id): %(exception)s' %
                {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id, 'exception': e})

    return HttpResponse('@SUCCESS', mimetype='text/plain')

@never_cache
@csrf_exempt
def getlist(request):
    emailaddr = request.GET.get('username', None)
    password = request.GET.get('password', None)

    user = auth(emailaddr, password)
    if user is None:
        return HttpResponse('@AUTHFAIL', mimetype='text/plain')

    dev = get_device(user, LEGACY_DEVICE_UID,
            request.META.get('HTTP_USER_AGENT', ''),
Exemple #12
0
def rewrite_podcasts(p_old, p_new):

    log('merging podcast %s "%s" to correct podcast %s "%s"' % (p_old.id, p_old.url, p_new.id, p_new.url))

    rewrite_newpodcast(p_old, p_new)
Exemple #13
0
                p_stats["deleted"] += 1

            except Exception, e:
                log("failed to delete podcast %s: %s" % (p.id, e))
                print "failed to delete podcast %s: %s" % (p.id, e)
                p_stats["error"] += 1

            continue

        try:
            su_podcast = Podcast.objects.get(url=su)

        except Podcast.DoesNotExist, e:
            # "target" podcast does not exist, we simply change the url
            if not dry_run:
                log('updating podcast %s - "%s" => "%s"' % (p.id, p.url, su))
                p.url = su
                p.save()

            p_stats["updated"] += 1
            continue

        # nothing to do
        if p == su_podcast:
            p_stats["unchanged"] += 1
            continue

        # last option - merge podcasts
        try:
            if not dry_run:
                rewrite_podcasts(p, su_podcast)