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)))
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')
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))
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
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)
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
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)
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})
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)
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
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', ''),
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)
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)