def monitor(request): params = QueryParams() params.add('format', default='html', choices=('html', 'plain')) params.add('deleted', type=boolish, default=False, choices=(True, False)) params.parse(request.GET) # If one of the parameters was invalid, redirect to a fixed url. # - QueryParams object will automatically set the parameter to a valid value. if params.invalid_value: return HttpResponseRedirect(reverse('notepad:monitor') + str(params)) if params['deleted']: pages = Page.objects.order_by('name') pages = sorted(pages, key=lambda page: page.name.lower()) else: notes = Note.objects.filter(deleted=False).distinct('page') pages = sorted([note.page for note in notes], key=lambda page: page.name.lower()) if params['format'] == 'plain': text = '\n'.join([page.name for page in pages]) return HttpResponse(text, content_type=settings.PLAINTEXT) else: if params['deleted']: deleted_query_str = str(params.but_with(deleted=False)) else: deleted_query_str = str(params.but_with(deleted=True)) context = { 'pages': pages, 'deleted': params['deleted'], 'deleted_query_str': deleted_query_str, } return render(request, 'notepad/monitor.tmpl', context)
def deleteitem(request, page): params = QueryParams() params.add('type', choices=('item', 'listitem')) params.add('key') params.add('id', type=int) params.parse(request.POST) item_type = ITEM_TYPES.get(params['type']) if not item_type: log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) item = get_by_key_or_id(item_type, page, params['key'], params['id']) item.deleted = True item.deleting_visit = request.visit item.note.deleted = True item.note.deleting_visit = request.visit item.save() item.note.save() return HttpResponseRedirect(get_view_url(page))
def deleteitemform(request, page): params = QueryParams() params.add('type', choices=('item', 'listitem')) params.add('key') params.add('id', type=int) params.parse(request.POST) item_type = ITEM_TYPES.get(params['type']) if not item_type: log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) item = get_by_key_or_id(item_type, page, params['key'], params['id']) context = { 'editing': True, 'deleting_text': True, 'item': item, 'type': params['type'] } return show_page(request, page, context)
def shares(request): params = QueryParams() params.add('version', type=int, choices=(1, 2, 3)) params.parse(request.GET) if not params['version']: return HttpResponseRedirect(reverse('horcrux:main')) threshold = THRESHOLDS[params['version']] share_nums = range(1, threshold+1) context = {'version':params['version'], 'share_nums':share_nums} return render(request, 'horcrux/shares.tmpl', context)
def monitor(request): # The query string parameters and their defaults. params = QueryParams() params.add('p', type=int, default=1, min=1) params.add('perpage', default=PER_PAGE_DEFAULT, type=int, min=1) params.add('include_me', type=boolish, default=False, choices=(True, False)) params.add('bot_thres', default=None, type=int) params.add('user', default=None, type=int) params.parse(request.GET) # If one of the parameters was invalid, redirect to a fixed url. # - QueryParams object will automatically set the parameter to a valid value. if params.invalid_value: return HttpResponseRedirect(reverse('traffic_monitor')+str(params)) admin = is_admin_and_secure(request) # Non-admins can only view their own traffic. if not admin: this_user = request.visit.visitor.user.id if params['user']: # If they gave a user, but it's not themselves, redirect back to themself. if params['user'] != this_user: return HttpResponseRedirect(reverse('traffic_monitor')+str(params.but_with(user=None))) else: # If they gave no user, that's fine. Silently show only themself. params['user'] = this_user params.params['user'].default = this_user # Obtain visits list from database. if params['user'] is not None: visits = Visit.objects.filter(visitor__user__id=params['user']) elif params['include_me']: visits = Visit.objects.all() else: visits = Visit.objects.exclude(visitor__user__id=1) # Exclude robots, if requested. if params['bot_thres'] is not None and params['user'] is None: visits = visits.filter(visitor__bot_score__lt=params['bot_thres']) # Order by id. visits = visits.order_by('-id') # Create a Paginator. pages = django.core.paginator.Paginator(visits, params['perpage']) try: page = pages.page(params['p']) except django.core.paginator.EmptyPage: return HttpResponseRedirect(reverse('traffic_monitor')+str(params.but_with(p=pages.num_pages))) # Construct the navigation links. links = collections.OrderedDict() if page.has_previous(): links['Later'] = str(params.but_with(p=page.previous_page_number())) if admin: if params['include_me']: links['Hide me'] = str(params.but_with(include_me=None)) else: links['Include me'] = str(params.but_with(include_me=True)) if params['bot_thres'] is not None and params['bot_thres'] != 0: links['Show all'] = str(params.but_with(bot_thres=None)) if params['bot_thres'] is None or params['bot_thres'] > categorize.SCORES['bot_in_ua']: links['Hide robots'] = str(params.but_with(bot_thres=categorize.SCORES['bot_in_ua'])) if params['bot_thres'] is None or params['bot_thres'] > categorize.SCORES['sent_cookies']+1: links['Show humans'] = str(params.but_with(bot_thres=categorize.SCORES['sent_cookies']+1)) if page.has_next(): links['Earlier'] = str(params.but_with(p=page.next_page_number())) context = { 'visits': page, 'admin':admin, 'links': links, 'this_visit': request.visit, 'timezone': set_timezone(request), # Set and retrieve timezone. 'ua_exact_thres': categorize.SCORES['ua_exact'], 'referrer_exact_thres': categorize.SCORES['referrer_exact'], } return render(request, 'traffic/monitor.tmpl', context)
def combine(request): params = QueryParams() params.add('version', type=int, choices=(1, 2, 3)) params.parse(request.POST) error = None used_server_share = False secrets = {'lastpass_email':settings.PERSONAL_EMAIL, 'accounts_link':settings.ACCOUNTS_LINK} threshold = THRESHOLDS[params['version']] try: # Gather the shares. shares = gather_shares(params, threshold, WORD_LIST_PATH) if params['version'] == 1: secrets['lastpass'] = combine_shares(shares, threshold, hex=False) elif params['version'] == 2: if os.path.exists(WORD_LIST_PATH): words_hex = combine_shares(shares, threshold, hex=True) word_map = convert.read_word_list(WORD_LIST_PATH) try: words = convert.hex_to_words(words_hex, word_map, group_length=GROUP_LENGTH) secrets['lastpass'] = '******'.join(words) except ValueError: error = ('Combined horcruxes to get {!r}, but could not convert to the actual password.' .format(words_hex)) else: error = "Couldn't find word list file on the server." #TODO: Detect if the password is just numbers, which indicates the user entered version 1 codes. elif params['version'] == 3: # Read in the share stored on the server, if needed. if len(shares) < threshold: try: share1 = read_share(SHARE1_PATH) shares.append(share1) used_server_share = True except HorcruxError as exception: log.error(exception.message) if len(shares) < threshold: raise HorcruxError('too_few_shares_and_missing_share1', 'User entered {} shares, but missing share1 means {} are needed.' .format(len(shares), threshold), value=len(shares), value2=threshold) # Combine the shares and get the passwords from the vault. try: vault_password = combine_shares(shares, threshold, hex=True) except HorcruxError as exception: if exception.type == 'too_few_shares' and used_server_share: exception.value -= 1 exception.value2 -= 1 raise exception if os.path.exists(VAULT_PATH): plaintext = decrypt_vault(VAULT_PATH, vault_password) secrets.update(parse_vault(plaintext)) else: error = ("Couldn't find encrypted vault file on the server, but successfully combined the " "horcruxes to obtain its password: {!r}".format(vault_password)) else: return HttpResponseRedirect(reverse('horcrux:main')) except HorcruxError as exception: log.error('HorcruxError {!r}: {}'.format(exception.type, exception.message)) error = ERROR_MESSAGES.get(exception.type, DEFAULT_ERROR).format(**vars(exception)) log.error(error) plural = params['version'] > 2 context = {'version':params['version'], 'secrets':secrets, 'plural':plural, 'error':error} return render(request, 'horcrux/combine.tmpl', context)
def view(request, page_name): params = QueryParams() params.add('note', type=int, default=None) params.add('version', type=int, default=None) params.add('format', default='html', choices=('html', 'plain')) params.add('admin', type=boolish, default=False, choices=(True, False)) params.add('archived', type=boolish, default=False, choices=(True, False)) params.add('deleted', type=boolish, default=False, choices=(True, False)) params.add('select', default='none', choices=('all', 'none')) params.parse(request.GET) # If one of the parameters was invalid, redirect to a fixed url. # - QueryParams object will automatically set the parameter to a valid value. if params.invalid_value: return HttpResponseRedirect( reverse('notepad:view', args=(page_name, )) + str(params)) # Only allow showing deleted notes to the admin over HTTPS. is_admin = is_admin_and_secure(request) if not is_admin and (params['admin'] or params['deleted']): query_str = str(params.but_with(admin=False, deleted=False)) return HttpResponseRedirect( reverse('notepad:view', args=(page_name, )) + query_str) # Fetch the note(s). if params['version']: notes = lib.get_notes_from_ids([params['version']], page_name, params['archived'], params['deleted']) elif params['note']: note = lib.get_latest_version(params['note'], page_name, params['archived'], params['deleted']) notes = [note] else: notes = lib.get_notes(page_name, params['archived'], params['deleted']) # Bundle up the data and display the notes. if params['format'] == 'plain': contents = [note.content for note in notes] return HttpResponse('\n\n'.join(contents), content_type=settings.PLAINTEXT) else: links = collections.OrderedDict() randomness = rand.randint(1, 1000000) links['☑ Select all'] = str( params.but_with(select='all', reload=randomness)) links['□ Select none'] = str( params.but_with(select='none', reload=randomness)) if is_admin: if params['admin']: links['Admin off'] = str(params.but_with(admin=False)) else: links['Admin on'] = str(params.but_with(admin=True)) if params['archived']: links['Hide archived'] = str(params.but_with(archived=False)) else: links['Show archived'] = str(params.but_with(archived=True)) if is_admin: if params['deleted']: links['Hide deleted'] = str(params.but_with(deleted=False)) else: links['Show deleted'] = str(params.but_with(deleted=True)) context = { 'page': page_name, 'notes': notes, 'links': links, 'admin_view': params['admin'], 'select': params['select'], } return render(request, 'notepad/view.tmpl', context)
def edititem(request, page): params = QueryParams() params.add('key') params.add('id', type=int) params.add('type', choices=('item', 'listitem')) params.add('content') params.add('title') params.add('body') params.add('attributes') params.parse(request.POST) log.info('Editing item: key {!r}, id {!r}'.format(params['key'], params['id'])) item_type = ITEM_TYPES.get(params['type']) if not item_type: log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) item = get_by_key_or_id(item_type, page, params['key'], params['id']) content = compose_content(params['title'], params['body'], params['content']) if item: log.info('Editing existing item.') edit_item(item, content, params['attributes'], request.visit) elif params['type'] == 'item': # Won't work for ListItems, but they can't be created via edititem anyway. log.info('Creating new item.') create_item(item_type, page, content, params['attributes'], request.visit, key=params['key']) else: log.error('Warning: Cannot edit nonexistent ListItem.') return HttpResponseRedirect(get_view_url(page))
def edititemform(request, page): params = QueryParams() params.add('key') params.add('id', type=int) params.add('type', choices=('item', 'listitem')) params.add('display-type') params.parse(request.POST) log.info('Composing edit form for item: key {!r}, id {!r}'.format( params['key'], params['id'])) item_type = ITEM_TYPES.get(params['type']) if not item_type: log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) item = get_by_key_or_id(item_type, page, params['key'], params['id']) if item is None: log.info('No item found. Using a dummy.') item = { 'id': params['id'], 'key': params['key'], 'note': { 'content': '' } } # Determine whether the item has a title, body, or neither. display_type = 'title-body' if params['display-type']: display_type = params['display-type'] elif params['key']: display_type_key = params['key'] + '-display-type' if params.get(display_type_key) is not None: display_type = params[display_type_key] context = { 'editing': True, 'editing_text': True, 'editing_item': True, 'admin': True, 'item': item, 'display_type': display_type, 'type': params['type'] } return show_page(request, page, context)
def moveitem(request, page): params = QueryParams() params.add('action', choices=('moveup', 'movedown')) params.add('type', choices=('item', 'listitem')) params.add('key') params.add('id', type=int) params.parse(request.POST) if params['type'] != 'listitem': log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) item_type = ITEM_TYPES.get(params['type']) item = get_by_key_or_id(item_type, page, params['key'], params['id']) if not item: log.error( f'No item found by key {params["key"]!r} or id {params["id"]}.') return HttpResponseRedirect(get_view_url(page)) if item.parent: siblings = item.parent.sorted_items() else: log.info('Item has no parent. Using root lists.') siblings = get_root_lists(page) if params['action'] == 'movedown': siblings = reversed(siblings) last_item = None for this_item in siblings: if this_item == item and last_item is not None: log.debug( f'Swapping order of Items {this_item.id} ({this_item.display_order}) and {last_item.id} ' f'({last_item.display_order}).') this_move = Move( type='position', item=this_item, old_display_order=this_item.display_order, new_display_order=last_item.display_order, visit=request.visit, ) last_move = Move( type='position', item=last_item, old_display_order=last_item.display_order, new_display_order=this_item.display_order, visit=request.visit, ) this_item.display_order = this_move.new_display_order last_item.display_order = last_move.new_display_order try: with transaction.atomic(): this_item.save() this_move.save() last_item.save() last_move.save() except DatabaseError as dbe: log.error(f'Error on saving moves: {dbe}') break last_item = this_item return HttpResponseRedirect(get_view_url(page))
def additem(request, page): params = QueryParams() params.add('type', choices=('item', 'listitem')) params.add('title') params.add('body') params.add('content') params.add('attributes') params.add('key') params.add('parent_key') params.add('parent_id', type=int) params.parse(request.POST) item_type = ITEM_TYPES.get(params['type']) if not item_type: log.error('Invalid type {!r}.'.format(request.POST.get('type'))) return HttpResponseRedirect(get_view_url(page)) content = compose_content(params['title'], params['body'], params['content']) parent_list = get_by_key_or_id(item_type, page, params['parent_key'], params['parent_id']) create_item(item_type, page, content, params['attributes'], request.visit, key=params['key'], parent_list=parent_list) return HttpResponseRedirect(get_view_url(page))