コード例 #1
0
ファイル: routes_list.py プロジェクト: 813492291816/BitChan
def list_bulk_add(list_address):
    global_admin, allow_msg = allowed_access(
        check_is_global_admin=True)
    board_list_admin, allow_msg = allowed_access(
        check_is_board_list_admin=True, check_admin_board=list_address)
    if not global_admin and not board_list_admin:
        return allow_msg

    form_list = forms_board.List()

    chan = Chan.query.filter(Chan.address == list_address).first()
    if not chan:
        return render_template("pages/404-board.html",
                               board_address=list_address)

    try:
        from_list = daemon_com.get_from_list(list_address)
    except:
        return render_template("pages/404-board.html",
                               board_address=list_address)

    board = {"current_chan": chan}
    status_msg = {"status_message": []}
    url = ""
    url_text = ""
    form_list_add = []

    try:
        this_chan_list = json.loads(chan.list)
    except:
        this_chan_list = {}

    chans = Chan.query.filter(and_(
        Chan.address != list_address,
        Chan.address.notin_(this_chan_list))).order_by(
            Chan.type.asc(),
            Chan.label.asc()).all()
    for each_chan in chans:
        str_select = ""
        if each_chan.type == "board":
            str_select += "Board: "
        elif each_chan.type == "list":
            str_select += "List: "
        str_select += each_chan.label
        if each_chan.access == "public":
            str_select += " [Public] "
        elif each_chan.access == "private":
            str_select += " [Private] "
        str_select += "({}...{})".format(
            each_chan.address[:9], each_chan.address[-6:])
        form_list_add.append((each_chan.address, str_select))

    if request.method == 'GET':
        if 'status_msg' in session:
            session.pop('status_msg')

    elif request.method == 'POST':
        add_bulk_list = []

        for each_input in request.form:
            if each_input == "add_bulk":
                for each_input in request.form:
                    if each_input.startswith("add_bulk_"):
                        add_bulk_list.append(each_input.split("_")[2])
                break

        # Add boards or lists to a list in bulk
        if form_list.add_bulk.data and add_bulk_list:
            if not global_admin and not board_list_admin:
                return allow_msg

            mod_list = Chan.query.filter(and_(
                Chan.type == "list",
                Chan.address == list_address)).first()

            dict_list_addresses = {}
            rules = {}

            if not mod_list:
                status_msg["status_message"].append("Invalid list to modify")
            else:
                if form_list.from_address.data:
                    mod_list.default_from_address = form_list.from_address.data

                try:
                    dict_list_addresses = json.loads(mod_list.list)
                except:
                    pass

                try:
                    rules = json.loads(mod_list.rules)
                except:
                    pass

                for each_address in add_bulk_list:
                    chan_add = Chan.query.filter(Chan.address == each_address).first()
                    if not chan_add:
                        status_msg["status_message"].append(
                            "Can't find board/list to add: {}".format(each_address))
                        continue

                    if form_list.add.data and each_address in dict_list_addresses:
                        status_msg["status_message"].append(
                            "Can't add address that's already on the list: {}".format(each_address))

                    if each_address == list_address:
                        status_msg["status_message"].append(
                            "Cannot modify an address that's the same address as the list: {}".format(each_address))

                    def sender_has_access(each_address, address_type):
                        access = get_access(each_address)
                        for each_address in daemon_com.get_identities():
                            if each_address in access[address_type]:
                                return True
                        for each_address in daemon_com.get_all_chans():
                            if each_address in access[address_type]:
                                return True

                    if mod_list.access == "private":
                        if (sender_has_access(list_address, "primary_addresses") or
                                sender_has_access(list_address, "secondary_addresses")):
                            # Primary and secondary access can add or delete from lists
                            modify_access = True
                        elif (form_list.add.data and
                                sender_has_access(list_address, "tertiary_addresses")):
                            # Only allow tertiary access to add to private lists
                            modify_access = True
                        else:
                            # Everyone else is prohibited from adding/deleting from private lists
                            modify_access = False

                        if not modify_access:
                            status_msg["status_message"].append(
                                "Cannot modify this list if you are not the owner.")

                    errors, dict_chan_info = process_passphrase(chan_add.passphrase)
                    if not dict_chan_info:
                        status_msg['status_message'].append(
                            "Error parsing passphrase for address {}".format(each_address))

                    if "allow_list_pgp_metadata" in rules and rules["allow_list_pgp_metadata"]:
                        if dict_chan_info["type"] in ["board", "list"]:
                            if len(chan_add.pgp_passphrase_msg) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Message PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_msg)))
                            elif not chan_add.pgp_passphrase_msg:
                                status_msg['status_message'].append(
                                    "Message PGP Passphrase of the entry you tried to add cannot be empty")
                        if dict_chan_info["type"] == "board":
                            if len(chan_add.pgp_passphrase_attach) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Attachment PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_attach)))
                            elif not chan_add.pgp_passphrase_attach:
                                status_msg['status_message'].append(
                                    "Attachment PGP Passphrase of the entry you tried to add cannot be empty")
                            if len(chan_add.pgp_passphrase_steg) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Steg PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_steg)))
                            elif not chan_add.pgp_passphrase_steg:
                                status_msg['status_message'].append(
                                    "Steg PGP Passphrase of the entry you tried to add cannot be empty")

                    dict_list_addresses[chan_add.address] = {
                        "passphrase": chan_add.passphrase
                    }
                    if "allow_list_pgp_metadata" in rules and rules["allow_list_pgp_metadata"]:
                        if dict_chan_info["type"] in ["board", "list"]:
                            dict_list_addresses[chan_add.address]["pgp_passphrase_msg"] = chan_add.pgp_passphrase_msg
                        if dict_chan_info["type"] == "board":
                            dict_list_addresses[chan_add.address]["pgp_passphrase_attach"] = chan_add.pgp_passphrase_attach
                            dict_list_addresses[chan_add.address]["pgp_passphrase_steg"] = chan_add.pgp_passphrase_steg

            if not status_msg['status_message']:
                status_msg["status_message"].append(
                    "Added to the List: {}".format(", ".join(add_bulk_list)))
                status_msg['status_title'] = "Success"
                url = "/list/{}".format(list_address)
                url_text = "Return to List"

                # Set the time the list changed
                if mod_list.list != json.dumps(dict_list_addresses):
                    mod_list.list_timestamp_changed = time.time()

                mod_list.list = json.dumps(dict_list_addresses)
                mod_list.list_send = True

                mod_list.save()

                add_mod_log_entry(
                    "Locally Added to List: {}".format(", ".join(add_bulk_list)),
                    message_id=None,
                    user_from=None,
                    board_address=list_address,
                    thread_hash=None)

                logger.info("Instructing send_lists() to run in {} minutes".format(
                    config.LIST_ADD_WAIT_TO_SEND_SEC / 60))
                daemon_com.update_timer_send_lists(config.LIST_ADD_WAIT_TO_SEND_SEC)

            return render_template("pages/alert.html",
                                   board=list_address,
                                   status_msg=status_msg,
                                   url=url,
                                   url_text=url_text)

        time.sleep(3)

        session['status_msg'] = status_msg

        if 'status_title' not in status_msg and status_msg['status_message']:
            status_msg['status_title'] = "Error"

    chan_posts = Chan.query.filter(Chan.type == "board").all()

    chan_lists = {}
    for each_chan in Chan.query.filter(Chan.type == "list").all():
        chan_lists[each_chan.address] = {
            "passphrase": each_chan.passphrase,
            "list": json.loads(each_chan.list)
        }
        if len(each_chan.label) > config.LABEL_LENGTH:
            chan_lists[each_chan.address]["label_short"] = each_chan.label[:config.LABEL_LENGTH]
        else:
            chan_lists[each_chan.address]["label_short"] = each_chan.label

    chan = Chan.query.filter(Chan.address == list_address).first()
    dict_join = {
        "passphrase": chan.passphrase
    }
    passphrase_base64 = base64.b64encode(
        json.dumps(dict_join).encode()).decode().replace("/", "&")
    if chan.pgp_passphrase_msg != config.PGP_PASSPHRASE_MSG:
        dict_join["pgp_msg"] = chan.pgp_passphrase_msg
    passphrase_base64_with_pgp = base64.b64encode(
        json.dumps(dict_join).encode()).decode().replace("/", "&")

    return render_template("pages/list_bulk_add.html",
                           board=board,
                           chan_lists=chan_lists,
                           chan_posts=chan_posts,
                           form_list=form_list,
                           form_list_add=form_list_add,
                           from_list=from_list,
                           passphrase_base64=passphrase_base64,
                           passphrase_base64_with_pgp=passphrase_base64_with_pgp,
                           status_msg=status_msg,
                           table_chan=Chan,
                           url=url,
                           url_text=url_text)
コード例 #2
0
ファイル: routes_list.py プロジェクト: 813492291816/BitChan
def join_from_list(list_address, join_address):
    global_admin, allow_msg = allowed_access(
        check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_join = forms_board.Join()

    status_msg = {"status_message": []}
    url = ""
    url_text = ""

    chan_list = Chan.query.filter(and_(
        Chan.type == "list",
        Chan.address == list_address)).first()
    try:
        dict_list_addresses = json.loads(chan_list.list)
    except:
        dict_list_addresses = {}
    try:
        rules = json.loads(chan_list.rules)
    except:
        rules = {}

    if join_address not in dict_list_addresses:
        status_msg['status_message'].append("Address to join not in list")

    dict_chan_info = {}
    passphrase = ""
    if "passphrase" in dict_list_addresses[join_address]:
        passphrase = dict_list_addresses[join_address]["passphrase"]

        if Chan.query.filter(Chan.passphrase == passphrase).count():
            status_msg['status_message'].append("Chan already in database")

        errors, dict_chan_info = process_passphrase(passphrase)
        if not dict_chan_info:
            status_msg['status_message'].append("Error parsing passphrase")
            for error in errors:
                status_msg['status_message'].append(error)

    if request.method == 'GET':
        if 'status_msg' in session:
            session.pop('status_msg')

    elif request.method == 'POST':
        if form_join.join.data:
            # Join from list
            if dict_chan_info and passphrase and not status_msg['status_message']:
                url = None
                url_text = None

                if dict_chan_info["rules"]:
                    dict_chan_info["rules"] = set_clear_time_to_future(dict_chan_info["rules"])

                new_chan = Chan()
                new_chan.passphrase = passphrase
                new_chan.access = dict_chan_info["access"]
                new_chan.type = dict_chan_info["type"]
                new_chan.primary_addresses = json.dumps(dict_chan_info["primary_addresses"])
                new_chan.secondary_addresses = json.dumps(dict_chan_info["secondary_addresses"])
                new_chan.tertiary_addresses = json.dumps(dict_chan_info["tertiary_addresses"])
                new_chan.rules = json.dumps(dict_chan_info["rules"])
                new_chan.label = dict_chan_info["label"]
                new_chan.description = dict_chan_info["description"]

                if form_join.pgp_passphrase_msg.data:
                    new_chan.pgp_passphrase_msg = form_join.pgp_passphrase_msg.data
                else:
                    new_chan.pgp_passphrase_msg = config.PGP_PASSPHRASE_MSG
                if new_chan.type == "board":
                    if form_join.pgp_passphrase_steg.data:
                        new_chan.pgp_passphrase_steg = form_join.pgp_passphrase_steg.data
                    else:
                        new_chan.pgp_passphrase_steg = config.PGP_PASSPHRASE_STEG
                    if form_join.pgp_passphrase_attach.data:
                        new_chan.pgp_passphrase_attach = form_join.pgp_passphrase_attach.data
                    else:
                        new_chan.pgp_passphrase_attach = config.PGP_PASSPHRASE_ATTACH

                result = daemon_com.join_chan(passphrase, clear_inventory=form_join.resync.data)
                if result.startswith("BM-"):
                    new_chan.address = result
                    new_chan.is_setup = True
                    if new_chan.type == "board":
                        status_msg['status_message'].append("Joined board")
                        url = "/board/{}/1".format(result)
                        url_text = "{} - {}".format(new_chan.label, result)
                    elif new_chan.type == "list":
                        status_msg['status_message'].append("Joined list")
                        url = "/list/{}".format(result)
                        url_text = "{} - {}".format(new_chan.label, result)
                else:
                    status_msg['status_message'].append("Could not join at this time: {}".format(result))
                    new_chan.address = None
                    new_chan.is_setup = False

                if 'status_title' not in status_msg:
                    status_msg['status_title'] = "Success"
                    status_msg['status_message'].append(result)
                    new_chan.save()

                return render_template("pages/alert.html",
                                       board=list_address,
                                       status_msg=status_msg,
                                       url=url,
                                       url_text=url_text)

        time.sleep(3)

        session['status_msg'] = status_msg

        if 'status_title' not in status_msg and status_msg['status_message']:
            status_msg['status_title'] = "Error"

    return render_template("pages/list_join.html",
                           dict_chan_info=dict_chan_info,
                           dict_list_addresses=dict_list_addresses,
                           form_join=form_join,
                           join_address=join_address,
                           rules=rules,
                           status_msg=status_msg,
                           url=url,
                           url_text=url_text)
コード例 #3
0
ファイル: routes_list.py プロジェクト: 813492291816/BitChan
def list_chans(list_address):
    allowed, allow_msg = allowed_access(check_can_view=True)
    if not allowed:
        return allow_msg

    form_list = forms_board.List()
    form_set = forms_board.SetChan()

    chan = Chan.query.filter(Chan.address == list_address).first()
    if not chan:
        return render_template("pages/404-board.html",
                               board_address=list_address)

    try:
        from_list = daemon_com.get_from_list(list_address)
    except:
        return render_template("pages/404-board.html",
                               board_address=list_address)

    board = {"current_chan": chan}
    status_msg = {"status_message": []}
    url = ""
    url_text = ""
    form_list_add = []

    try:
        this_chan_list = json.loads(chan.list)
    except:
        this_chan_list = {}

    chans = Chan.query.filter(and_(
        Chan.address != list_address,
        Chan.address.notin_(this_chan_list))).order_by(
            Chan.type.asc(),
            Chan.label.asc()).all()
    for each_chan in chans:
        str_select = ""
        if each_chan.type == "board":
            str_select += "Board: "
        elif each_chan.type == "list":
            str_select += "List: "
        str_select += each_chan.label
        if each_chan.access == "public":
            str_select += " [Public] "
        elif each_chan.access == "private":
            str_select += " [Private] "
        str_select += "({}...{})".format(
            each_chan.address[:9], each_chan.address[-6:])
        form_list_add.append((each_chan.address, str_select))

    if request.method == 'GET':
        if 'status_msg' in session:
            session.pop('status_msg')

    elif request.method == 'POST':
        global_admin, allow_msg = allowed_access(
            check_is_global_admin=True)
        if not global_admin:
            return allow_msg

        join_bulk = None
        join_bulk_list = []
        join = None
        delete = None

        for each_input in request.form:
            if each_input.startswith("join_"):
                join = each_input.split("_")[1]
                break
            elif each_input.startswith("delete_"):
                delete = each_input.split("_")[1]
                break
            elif each_input == "joinbulk":
                join_bulk = True
                break

        if join_bulk:
            for each_input in request.form:
                if each_input.startswith("joinbulk_"):
                    join_bulk_list.append(each_input.split("_")[1])

        if form_set.set_pgp_passphrase_msg.data:
            global_admin, allow_msg = allowed_access(
                check_is_global_admin=True)
            if not global_admin:
                return allow_msg

            if not form_set.pgp_passphrase_msg.data:
                status_msg['status_message'].append("Message PGP passphrase required")
            elif len(form_set.pgp_passphrase_msg.data) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append("Message PGP passphrase longer than {}: {}".format(
                    config.PGP_PASSPHRASE_LENGTH, len(form_set.pgp_passphrase_msg.data)))
            else:
                chan.pgp_passphrase_msg = form_set.pgp_passphrase_msg.data
                chan.save()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Changed PGP Passphrase.")

        # set default/preferred address to update list
        elif form_list.save_from.data:
            global_admin, allow_msg = allowed_access(
                check_is_global_admin=True)
            if not global_admin:
                return allow_msg

            chan = Chan.query.filter(
                Chan.address == list_address).first()
            if chan:
                if form_list.from_address.data:
                    chan.default_from_address = form_list.from_address.data
                else:
                    chan.default_from_address = None
                chan.save()

        # Add/delete a board or list to/from a list
        elif form_list.add.data or delete:
            global_admin, allow_msg = allowed_access(
                check_is_global_admin=True)
            board_list_admin, allow_msg = allowed_access(
                check_is_board_list_admin=True, check_admin_board=list_address)
            if not global_admin and not board_list_admin:
                return allow_msg

            address = None
            if form_list.add.data:
                address = form_list.address.data
            elif delete:
                address = delete

            chan_add = Chan.query.filter(Chan.address == address).first()
            if form_list.add.data and not chan_add:
                status_msg["status_message"].append("Invalid list to modify")
            else:
                mod_list = Chan.query.filter(and_(
                    Chan.type == "list",
                    Chan.address == list_address)).first()

                try:
                    dict_list_addresses = json.loads(mod_list.list)
                except:
                    dict_list_addresses = {}

                try:
                    rules = json.loads(mod_list.rules)
                except:
                    rules = {}

                if form_list.add.data and address in dict_list_addresses:
                    status_msg["status_message"].append("Can't add address that's already on the list")

                if form_list.delete.data and address not in dict_list_addresses:
                    status_msg["status_message"].append("Can't delete address that's not on the list")

                if address == list_address:
                    status_msg["status_message"].append("Cannot modify an address that's the same address as the list")

                def sender_has_access(address, address_type):
                    access = get_access(address)
                    for each_address in daemon_com.get_identities():
                        if each_address in access[address_type]:
                            return True
                    for each_address in daemon_com.get_all_chans():
                        if each_address in access[address_type]:
                            return True

                if mod_list.access == "private":
                    if (sender_has_access(list_address, "primary_addresses") or
                            sender_has_access(list_address, "secondary_addresses")):
                        # Primary and secondary access can add or delete from lists
                        modify_access = True
                    elif (form_list.add.data and
                            sender_has_access(list_address, "tertiary_addresses")):
                        # Only allow tertiary access to add to private lists
                        modify_access = True
                    else:
                        # Everyone else is prohibited from adding/deleting from private lists
                        modify_access = False

                    if not modify_access:
                        status_msg["status_message"].append(
                            "Cannot modify this list if you are not the owner.")

                # Check if passphrase is valid
                if form_list.add.data:
                    errors, dict_chan_info = process_passphrase(chan_add.passphrase)
                    if not dict_chan_info:
                        status_msg['status_message'].append("Error parsing passphrase")

                    if "allow_list_pgp_metadata" in rules and rules["allow_list_pgp_metadata"]:
                        if dict_chan_info["type"] in ["board", "list"]:
                            if len(chan_add.pgp_passphrase_msg) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Message PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_msg)))
                            elif not chan_add.pgp_passphrase_msg:
                                status_msg['status_message'].append(
                                    "Message PGP Passphrase of the entry you tried to add cannot be empty")
                        if dict_chan_info["type"] == "board":
                            if len(chan_add.pgp_passphrase_attach) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Attachment PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_attach)))
                            elif not chan_add.pgp_passphrase_attach:
                                status_msg['status_message'].append(
                                    "Attachment PGP Passphrase of the entry you tried to add cannot be empty")
                            if len(chan_add.pgp_passphrase_steg) > config.PGP_PASSPHRASE_LENGTH:
                                status_msg['status_message'].append(
                                    "Steg PGP Passphrase longer than {}: {}".format(
                                        config.PGP_PASSPHRASE_LENGTH, len(chan_add.pgp_passphrase_steg)))
                            elif not chan_add.pgp_passphrase_steg:
                                status_msg['status_message'].append(
                                    "Steg PGP Passphrase of the entry you tried to add cannot be empty")

                if not status_msg['status_message']:
                    status_msg['status_title'] = "Success"

                    if form_list.add.data:
                        dict_list_addresses[chan_add.address] = {
                            "passphrase": chan_add.passphrase
                        }
                        if "allow_list_pgp_metadata" in rules and rules["allow_list_pgp_metadata"]:
                            if dict_chan_info["type"] in ["board", "list"]:
                                dict_list_addresses[chan_add.address]["pgp_passphrase_msg"] = chan_add.pgp_passphrase_msg
                            if dict_chan_info["type"] == "board":
                                dict_list_addresses[chan_add.address]["pgp_passphrase_attach"] = chan_add.pgp_passphrase_attach
                                dict_list_addresses[chan_add.address]["pgp_passphrase_steg"] = chan_add.pgp_passphrase_steg
                        status_msg["status_message"].append(
                            "Added {} to the List".format(address))

                        add_mod_log_entry(
                            "Locally Added to List: {}".format(address),
                            message_id=None,
                            user_from=None,
                            board_address=list_address,
                            thread_hash=None)

                    elif form_list.delete.data:
                        dict_list_addresses.pop(address)
                        status_msg["status_message"].append(
                            "Deleted {} from the List".format(address))

                        add_mod_log_entry(
                            "Locally Deleted from List: {}".format(address),
                            message_id=None,
                            user_from=None,
                            board_address=list_address,
                            thread_hash=None)

                    # Set the time the list changed
                    if mod_list.list != json.dumps(dict_list_addresses):
                        mod_list.list_timestamp_changed = time.time()

                    mod_list.list = json.dumps(dict_list_addresses)
                    mod_list.list_send = True
                    mod_list.save()

                    time_to_send = 60 * 1
                    logger.info("Instructing send_lists() to run in {} minutes".format(time_to_send / 60))
                    daemon_com.update_timer_send_lists(time_to_send)

        elif join:
            # Join from list
            global_admin, allow_msg = allowed_access(
                check_is_global_admin=True)
            if not global_admin:
                return allow_msg

            return redirect(url_for("routes_list.join_from_list",
                                    list_address=list_address,
                                    join_address=join))

        elif join_bulk:
            # Bulk join from list
            global_admin, allow_msg = allowed_access(
                check_is_global_admin=True)
            if not global_admin:
                return allow_msg

            if not join_bulk_list:
                status_msg['status_title'] = "Error"
                status_msg["status_message"].append("You must check at least one list entry to join")
            else:
                daemon_com.bulk_join(list_address, join_bulk_list)

            status_msg['status_title'] = "Success"
            status_msg["status_message"].append(
                "Addresses being joined in the background. "
                "Give the process time to complete.")

        time.sleep(3)

        session['status_msg'] = status_msg

        if 'status_title' not in status_msg and status_msg['status_message']:
            status_msg['status_title'] = "Error"

    chan_posts = Chan.query.filter(Chan.type == "board").all()

    chan_lists = {}
    for each_chan in Chan.query.filter(Chan.type == "list").all():
        chan_lists[each_chan.address] = {
            "passphrase": each_chan.passphrase,
            "list": json.loads(each_chan.list)
        }
        if len(each_chan.label) > config.LABEL_LENGTH:
            chan_lists[each_chan.address]["label_short"] = each_chan.label[:config.LABEL_LENGTH]
        else:
            chan_lists[each_chan.address]["label_short"] = each_chan.label

    chan = Chan.query.filter(Chan.address == list_address).first()
    dict_join = {
        "passphrase": chan.passphrase
    }
    passphrase_base64 = base64.b64encode(
        json.dumps(dict_join).encode()).decode().replace("/", "&")
    if chan.pgp_passphrase_msg != config.PGP_PASSPHRASE_MSG:
        dict_join["pgp_msg"] = chan.pgp_passphrase_msg
    passphrase_base64_with_pgp = base64.b64encode(
        json.dumps(dict_join).encode()).decode().replace("/", "&")

    return render_template("pages/list.html",
                           board=board,
                           chan_lists=chan_lists,
                           chan_posts=chan_posts,
                           form_list=form_list,
                           form_list_add=form_list_add,
                           from_list=from_list,
                           passphrase_base64=passphrase_base64,
                           passphrase_base64_with_pgp=passphrase_base64_with_pgp,
                           status_msg=status_msg,
                           url=url,
                           url_text=url_text)
コード例 #4
0
def join_base64(passphrase_base64):
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    pgp_passphrase_msg = config.PGP_PASSPHRASE_MSG
    pgp_passphrase_attach = config.PGP_PASSPHRASE_ATTACH
    pgp_passphrase_steg = config.PGP_PASSPHRASE_STEG
    dict_chan_info = None
    status_msg = {"status_message": []}
    url = ""
    url_text = ""
    chan_exists = None
    stage = "join_passphrase"

    form_join = forms_board.Join()

    try:
        passphrase_dict_json = base64.b64decode(
            passphrase_base64.replace("&", "/")).decode()
        passphrase_dict = json.loads(passphrase_dict_json)
        passphrase_json = passphrase_dict["passphrase"]
        if "pgp_msg" in passphrase_dict:
            pgp_passphrase_msg = passphrase_dict["pgp_msg"]
        if "pgp_attach" in passphrase_dict:
            pgp_passphrase_attach = passphrase_dict["pgp_attach"]
        if "pgp_steg" in passphrase_dict:
            pgp_passphrase_steg = passphrase_dict["pgp_steg"]
        chan_exists = Chan.query.filter(
            Chan.passphrase == passphrase_json).first()

        errors, dict_chan_info = process_passphrase(passphrase_json)
        if not dict_chan_info:
            status_msg['status_message'].append("Error parsing passphrase")
            for error in errors:
                status_msg['status_message'].append(error)
    except Exception as err:
        status_msg['status_message'].append(
            "Issue parsing base64 string: {}".format(err))

    if request.method == 'POST':
        if form_join.join.data:

            if not status_msg['status_message']:
                for each_word in RESTRICTED_WORDS:
                    if each_word in dict_chan_info["label"].lower():
                        status_msg['status_message'].append(
                            "bitchan is a restricted word for labels")

            if not status_msg['status_message']:
                result = daemon_com.join_chan(
                    passphrase_json, clear_inventory=form_join.resync.data)

                if dict_chan_info["rules"]:
                    dict_chan_info["rules"] = set_clear_time_to_future(
                        dict_chan_info["rules"])

                if form_join.pgp_passphrase_msg.data:
                    pgp_passphrase_msg = form_join.pgp_passphrase_msg.data
                if form_join.pgp_passphrase_attach.data:
                    pgp_passphrase_attach = form_join.pgp_passphrase_attach.data
                if form_join.pgp_passphrase_steg.data:
                    pgp_passphrase_steg = form_join.pgp_passphrase_steg.data

                new_chan = Chan()
                new_chan.passphrase = passphrase_json
                new_chan.access = dict_chan_info["access"]
                new_chan.type = dict_chan_info["type"]
                new_chan.label = dict_chan_info["label"]
                new_chan.description = dict_chan_info["description"]
                new_chan.pgp_passphrase_msg = pgp_passphrase_msg
                if dict_chan_info["type"] == "board":
                    new_chan.pgp_passphrase_attach = pgp_passphrase_attach
                    new_chan.pgp_passphrase_steg = pgp_passphrase_steg
                new_chan.primary_addresses = json.dumps(
                    dict_chan_info["primary_addresses"])
                new_chan.secondary_addresses = json.dumps(
                    dict_chan_info["secondary_addresses"])
                new_chan.tertiary_addresses = json.dumps(
                    dict_chan_info["tertiary_addresses"])
                new_chan.restricted_addresses = json.dumps(
                    dict_chan_info["restricted_addresses"])
                new_chan.rules = json.dumps(dict_chan_info["rules"])

                log_description = None

                if result.startswith("BM-"):
                    new_chan.address = result
                    new_chan.is_setup = True
                    if new_chan.type == "board":
                        status_msg['status_message'].append("Joined board")
                        url = "/board/{}/1".format(result)
                        url_text = "/{}/ - {}".format(new_chan.label,
                                                      new_chan.description)
                        log_description = "Joined board {} ({})".format(
                            url_text, result)
                    elif new_chan.type == "list":
                        status_msg['status_message'].append("Joined list")
                        url = "/list/{}".format(result)
                        url_text = "{} - {}".format(new_chan.label,
                                                    new_chan.description)
                        log_description = "Joined list {} ({})".format(
                            url_text, result)
                else:
                    status_msg['status_message'].append("Creation queued")
                    new_chan.address = None
                    new_chan.is_setup = False

                if log_description:
                    add_mod_log_entry(log_description,
                                      message_id=None,
                                      user_from=None,
                                      board_address=result,
                                      thread_hash=None)

                if 'status_title' not in status_msg:
                    status_msg['status_title'] = "Success"
                    new_chan.save()
                    stage = "end"

    if 'status_title' not in status_msg and status_msg['status_message']:
        status_msg['status_title'] = "Error"

    return render_template("pages/join.html",
                           chan_exists=chan_exists,
                           dict_chan_info=dict_chan_info,
                           form_join=form_join,
                           passphrase=passphrase_json,
                           pgp_passphrase_msg=pgp_passphrase_msg,
                           pgp_passphrase_attach=pgp_passphrase_attach,
                           pgp_passphrase_steg=pgp_passphrase_steg,
                           stage=stage,
                           status_msg=status_msg,
                           url=url,
                           url_text=url_text)
コード例 #5
0
def join():
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_join = forms_board.Join()

    status_msg = {"status_message": []}
    url = ""
    url_text = ""

    if not form_join.stage.data:
        stage = "start"
    else:
        stage = form_join.stage.data

    if request.method == 'POST':
        if stage == "start":
            if form_join.join_type.data == "join":
                stage = "join"
            elif form_join.join_type.data == "public_board":
                stage = "public_board"
            elif form_join.join_type.data == "private_board":
                stage = "private_board"
            elif form_join.join_type.data == "public_list":
                stage = "public_list"
            elif form_join.join_type.data == "private_list":
                stage = "private_list"

        # Join board or list
        elif stage == "join" and form_join.join.data:
            if not form_join.passphrase.data:
                status_msg['status_message'].append("Passphrase required")

            passphrase = None
            try:
                passphrase_dict = json.loads(form_join.passphrase.data)
                passphrase = json.dumps(passphrase_dict)
            except:
                status_msg['status_message'].append(
                    "Passphrase does not represent valid JSON")

            # Check if already a member of board/list with passphrase
            chan = Chan.query.filter(Chan.passphrase == passphrase).first()
            if chan:
                status_msg['status_message'].append(
                    "You are already a member of this board or list.")

            if not form_join.pgp_passphrase_msg.data:
                status_msg['status_message'].append(
                    "Message PGP passphrase required")
            elif len(form_join.pgp_passphrase_msg.data
                     ) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append(
                    "Message PGP passphrase too long. Max: {}".format(
                        config.PGP_PASSPHRASE_LENGTH))

            if not form_join.pgp_passphrase_attach.data:
                status_msg['status_message'].append(
                    "Attachment PGP passphrase required. "
                    "If it's a list you're joining, just leave the default option."
                )
            elif len(form_join.pgp_passphrase_attach.data
                     ) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append(
                    "Attachment PGP passphrase too long. Max: {}".format(
                        config.PGP_PASSPHRASE_LENGTH))

            if not form_join.pgp_passphrase_steg.data:
                status_msg['status_message'].append(
                    "Steg PGP passphrase required"
                    "If it's a list you're joining, just leave the default option."
                )
            elif len(form_join.pgp_passphrase_attach.data
                     ) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append(
                    "Steg PGP passphrase too long. Max: {}".format(
                        config.PGP_PASSPHRASE_LENGTH))

            errors, dict_chan_info = process_passphrase(passphrase)
            if not dict_chan_info:
                status_msg['status_message'].append("Error parsing passphrase")
                for error in errors:
                    status_msg['status_message'].append(error)

            if not status_msg['status_message']:
                for each_word in RESTRICTED_WORDS:
                    if each_word in dict_chan_info["label"].lower():
                        status_msg['status_message'].append(
                            "bitchan is a restricted word for labels")

            if not status_msg['status_message']:
                result = daemon_com.join_chan(
                    passphrase, clear_inventory=form_join.resync.data)

                if dict_chan_info["rules"]:
                    dict_chan_info["rules"] = set_clear_time_to_future(
                        dict_chan_info["rules"])

                new_chan = Chan()
                new_chan.passphrase = passphrase
                new_chan.access = dict_chan_info["access"]
                new_chan.type = dict_chan_info["type"]
                new_chan.label = dict_chan_info["label"]
                new_chan.description = dict_chan_info["description"]
                new_chan.primary_addresses = json.dumps(
                    dict_chan_info["primary_addresses"])
                new_chan.secondary_addresses = json.dumps(
                    dict_chan_info["secondary_addresses"])
                new_chan.tertiary_addresses = json.dumps(
                    dict_chan_info["tertiary_addresses"])
                new_chan.restricted_addresses = json.dumps(
                    dict_chan_info["restricted_addresses"])
                new_chan.rules = json.dumps(dict_chan_info["rules"])
                new_chan.pgp_passphrase_msg = form_join.pgp_passphrase_msg.data
                new_chan.pgp_passphrase_attach = form_join.pgp_passphrase_attach.data
                new_chan.pgp_passphrase_steg = form_join.pgp_passphrase_steg.data

                log_description = None

                if result.startswith("BM-"):
                    new_chan.address = result
                    new_chan.is_setup = True
                    if new_chan.type == "board":
                        status_msg['status_message'].append("Joined board")
                        url = "/board/{}/1".format(result)
                        url_text = "/{}/ - {}".format(new_chan.label,
                                                      new_chan.description)
                        log_description = "Joined board {} ({})".format(
                            url_text, result)
                    elif new_chan.type == "list":
                        status_msg['status_message'].append("Joined list")
                        url = "/list/{}".format(result)
                        url_text = "{} - {}".format(new_chan.label,
                                                    new_chan.description)
                        log_description = "Joined list {} ({})".format(
                            url_text, result)
                else:
                    status_msg['status_message'].append(
                        "Chan creation queued.")
                    new_chan.address = None
                    new_chan.is_setup = False

                if log_description:
                    add_mod_log_entry(log_description,
                                      message_id=None,
                                      user_from=None,
                                      board_address=result,
                                      thread_hash=None)

                if 'status_title' not in status_msg:
                    status_msg['status_title'] = "Success"
                    new_chan.save()
                    stage = "end"

        # Create public/private board/list
        elif (stage in [
                "public_board", "private_board", "public_list", "private_list"
        ] and form_join.join.data):

            if not form_join.pgp_passphrase_msg.data:
                status_msg['status_message'].append(
                    "Message PGP passphrase required")
            elif len(form_join.pgp_passphrase_msg.data
                     ) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append(
                    "Message PGP passphrase too long. Max: {}".format(
                        config.PGP_PASSPHRASE_LENGTH))

            if stage in ["public_board", "private_board"]:
                if not form_join.pgp_passphrase_attach.data:
                    status_msg['status_message'].append(
                        "Attachment PGP passphrase required")
                elif len(form_join.pgp_passphrase_attach.data
                         ) > config.PGP_PASSPHRASE_LENGTH:
                    status_msg['status_message'].append(
                        "Attachment PGP passphrase too long. Max: {}".format(
                            config.PGP_PASSPHRASE_LENGTH))

                if not form_join.pgp_passphrase_steg.data:
                    status_msg['status_message'].append(
                        "Steg PGP passphrase required")
                elif len(form_join.pgp_passphrase_steg.data
                         ) > config.PGP_PASSPHRASE_LENGTH:
                    status_msg['status_message'].append(
                        "Steg PGP passphrase too long. Max: {}".format(
                            config.PGP_PASSPHRASE_LENGTH))

            label = form_join.label.data
            if not label:
                status_msg['status_message'].append("Label required")
            elif len(label) > config.LABEL_LENGTH:
                status_msg['status_message'].append(
                    "Label too long. Must be {} or fewer characters.".format(
                        config.LABEL_LENGTH))
            for each_word in RESTRICTED_WORDS:
                if each_word in label.lower():
                    status_msg['status_message'].append(
                        "bitchan is a restricted word for labels")

            description = form_join.description.data
            if not description:
                status_msg['status_message'].append("Description required")
            elif len(description) > config.DESCRIPTION_LENGTH:
                status_msg['status_message'].append(
                    "Description too long. Must be {} or fewer characters.".
                    format(config.DESCRIPTION_LENGTH))

            def process_additional_addresses(form_list, status_msg):
                add_list_failed = []
                add_list_passed = []
                try:
                    list_additional = form_list.split(",")
                    for each_ident in list_additional:
                        ident_strip = each_ident.replace(" ", "")
                        if (ident_strip and (not ident_strip.startswith("BM-")
                                             or len(ident_strip) > 38
                                             or len(ident_strip) < 34)):
                            add_list_failed.append(ident_strip)
                        elif ident_strip.startswith("BM-"):
                            add_list_passed.append(ident_strip)
                except:
                    logger.exception(1)
                    status_msg['status_message'].append(
                        "Error parsing additional addresses. "
                        "Must only be comma-separated addresses without spaces."
                    )
                return status_msg, add_list_failed, add_list_passed

            status_msg, add_list_prim_fail, add_list_prim_pass = process_additional_addresses(
                form_join.primary_additional.data, status_msg)
            if add_list_prim_fail:
                status_msg['status_message'].append(
                    "Error parsing primary additional identities. "
                    "Must only be comma-separated addresses without spaces.")
            if len(",".join(
                    add_list_prim_pass)) > config.PASSPHRASE_ADDRESSES_LENGTH:
                status_msg['status_message'].append(
                    "Owner Address list is greater than {} characters: {}".
                    format(config.PASSPHRASE_ADDRESSES_LENGTH,
                           len(",".join(add_list_prim_pass))))

            list_primary_identities = []
            for key in request.form.keys():
                if 'primary_identity_' in key and key[17:]:
                    list_primary_identities.append(key[17:])
            list_primary_address_book = []
            for key in request.form.keys():
                if 'primary_address_book_' in key and key[21:]:
                    list_primary_address_book.append(key[21:])
            list_primary_chans = []
            for key in request.form.keys():
                if 'primary_chans_' in key and key[14:]:
                    list_primary_chans.append(key[14:])
            list_primary_addresses = (list_primary_identities +
                                      list_primary_address_book +
                                      list_primary_chans + add_list_prim_pass)

            status_msg, add_list_sec_fail, add_list_sec_pass = process_additional_addresses(
                form_join.secondary_additional.data, status_msg)
            if add_list_prim_fail:
                status_msg['status_message'].append(
                    "Error parsing secondary additional identities. "
                    "Must only be comma-separated addresses without spaces.")
            if len(",".join(
                    add_list_sec_pass)) > config.PASSPHRASE_ADDRESSES_LENGTH:
                status_msg['status_message'].append(
                    "Admin Address list is greater than {} characters: {}".
                    format(config.PASSPHRASE_ADDRESSES_LENGTH,
                           len(",".join(add_list_sec_pass))))

            list_secondary_identities = []
            for key in request.form.keys():
                if 'secondary_identity_' in key and key[19:]:
                    list_secondary_identities.append(key[19:])
            list_secondary_address_book = []
            for key in request.form.keys():
                if 'secondary_address_book_' in key and key[23:]:
                    list_secondary_address_book.append(key[23:])
            list_secondary_chans = []
            for key in request.form.keys():
                if 'secondary_chans_' in key and key[16:]:
                    list_secondary_chans.append(key[16:])
            list_secondary_addresses = (list_secondary_identities +
                                        list_secondary_address_book +
                                        list_secondary_chans +
                                        add_list_sec_pass)

            status_msg, add_list_ter_fail, add_list_ter_pass = process_additional_addresses(
                form_join.tertiary_additional.data, status_msg)
            if add_list_prim_fail:
                status_msg['status_message'].append(
                    "Error parsing tertiary additional identities. "
                    "Must only be comma-separated addresses without spaces.")
            if len(",".join(
                    add_list_ter_pass)) > config.PASSPHRASE_ADDRESSES_LENGTH:
                status_msg['status_message'].append(
                    "User Address list is greater than {} characters: {}".
                    format(config.PASSPHRASE_ADDRESSES_LENGTH,
                           len(",".join(add_list_ter_pass))))

            list_tertiary_identities = []
            for key in request.form.keys():
                if 'tertiary_identity_' in key and key[18:]:
                    list_tertiary_identities.append(key[18:])
            list_tertiary_address_book = []
            for key in request.form.keys():
                if 'tertiary_address_book_' in key and key[22:]:
                    list_tertiary_address_book.append(key[22:])
            list_tertiary_chans = []
            for key in request.form.keys():
                if 'tertiary_chans_' in key and key[15:]:
                    list_tertiary_chans.append(key[15:])
            list_tertiary_addresses = (list_tertiary_identities +
                                       list_tertiary_address_book +
                                       list_tertiary_chans + add_list_ter_pass)

            status_msg, add_list_restricted_fail, add_list_restricted_pass = process_additional_addresses(
                form_join.restricted_additional.data, status_msg)
            if add_list_restricted_fail:
                status_msg['status_message'].append(
                    "Error parsing restricted additional identities. "
                    "Must only be comma-separated addresses without spaces.")

            list_restricted_address_book = []
            for key in request.form.keys():
                if 'restricted_address_book_' in key and key[24:]:
                    list_restricted_address_book.append(key[24:])
            list_restricted_chans = []
            for key in request.form.keys():
                if 'restricted_chans_' in key and key[17:]:
                    list_restricted_chans.append(key[17:])
            list_restricted_addresses = (list_restricted_address_book +
                                         list_restricted_chans +
                                         add_list_restricted_pass)

            if (stage in ["private_board", "private_list"]
                    and not list_primary_addresses):
                status_msg['status_message'].append(
                    "Must provide at least one primary address as the owner")

            rules = {}

            if form_join.require_identity_to_post.data:
                rules[
                    "require_identity_to_post"] = form_join.require_identity_to_post.data
            if form_join.automatic_wipe.data:
                if form_join.wipe_epoch.data > config.WIPE_START_MAX:
                    status_msg['status_message'].append(
                        "Automatic Wipe Epoch Start Time is greater than year 3020."
                    )
                if form_join.interval_seconds.data > config.WIPE_INTERVAL_MAX:
                    status_msg['status_message'].append(
                        "Automatic Wipe Interval is greater than 500 years.")
                try:
                    rules["automatic_wipe"] = {
                        "wipe_epoch": form_join.wipe_epoch.data,
                        "interval_seconds": form_join.interval_seconds.data
                    }
                except:
                    status_msg['status_message'].append(
                        "Could not process Rule options to Automatic Wipe")
            if form_join.allow_list_pgp_metadata.data:
                rules[
                    "allow_list_pgp_metadata"] = form_join.allow_list_pgp_metadata.data

            extra_string = form_join.extra_string.data
            if len(extra_string) > config.PASSPHRASE_ADDRESSES_LENGTH:
                status_msg['status_message'].append(
                    "Extra String is greater than {} characters: {}".format(
                        config.PASSPHRASE_EXTRA_STRING_LENGTH,
                        len(extra_string)))

            if not status_msg['status_message']:
                access = stage.split("_")[0]
                chan_type = stage.split("_")[1]

                passphrase = generate_passphrase(
                    access, chan_type, label, description,
                    list_restricted_addresses, list_primary_addresses,
                    list_secondary_addresses, list_tertiary_addresses, rules,
                    extra_string)

                # Check generated passphrase
                errors, dict_chan_info = process_passphrase(passphrase)
                if not dict_chan_info:
                    status_msg['status_message'].append(
                        "Error parsing passphrase")
                    for error in errors:
                        status_msg['status_message'].append(error)

            if not status_msg['status_message']:
                result = daemon_com.join_chan(
                    passphrase, clear_inventory=form_join.resync.data)

                if rules:
                    rules = set_clear_time_to_future(rules)

                new_chan = Chan()
                new_chan.access = access
                new_chan.type = chan_type
                new_chan.label = label
                new_chan.description = description
                new_chan.passphrase = passphrase
                new_chan.restricted_addresses = json.dumps(
                    list_restricted_addresses)
                new_chan.primary_addresses = json.dumps(list_primary_addresses)
                new_chan.secondary_addresses = json.dumps(
                    list_secondary_addresses)
                new_chan.tertiary_addresses = json.dumps(
                    list_tertiary_addresses)
                new_chan.rules = json.dumps(rules)
                new_chan.pgp_passphrase_msg = form_join.pgp_passphrase_msg.data
                new_chan.pgp_passphrase_attach = form_join.pgp_passphrase_attach.data
                new_chan.pgp_passphrase_steg = form_join.pgp_passphrase_steg.data

                log_description = None

                if result.startswith("BM-"):
                    if stage == "public_board":
                        status_msg['status_message'].append(
                            "Created public board")
                    elif stage == "private_board":
                        status_msg['status_message'].append(
                            "Created private board")
                    elif stage == "public_list":
                        status_msg['status_message'].append(
                            "Created public list")
                    elif stage == "private_list":
                        status_msg['status_message'].append(
                            "Created private list")
                    new_chan.address = result
                    new_chan.is_setup = True
                    if stage in ["public_board", "private_board"]:
                        url = "/board/{}/1".format(result)
                        url_text = "/{}/ - {}".format(label, description)
                        log_description = "Created board {} ({})".format(
                            url_text, result)
                    elif stage in ["public_list", "private_list"]:
                        url = "/list/{}".format(result)
                        url_text = "{} - {}".format(label, description)
                        log_description = "Created list {} ({})".format(
                            url_text, result)
                else:
                    status_msg['status_message'].append("Creation queued")
                    new_chan.address = None
                    new_chan.is_setup = False

                if log_description:
                    add_mod_log_entry(log_description,
                                      message_id=None,
                                      user_from=None,
                                      board_address=result,
                                      thread_hash=None)

                if 'status_title' not in status_msg:
                    status_msg['status_title'] = "Success"
                    new_chan.save()
                    stage = "end"

        if 'status_title' not in status_msg and status_msg['status_message']:
            status_msg['status_title'] = "Error"

    return render_template("pages/join.html",
                           stage=stage,
                           status_msg=status_msg,
                           url=url,
                           url_text=url_text)
コード例 #6
0
def identities():
    global_admin, allow_msg = allowed_access(
        check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_identity = forms_settings.Identity()
    form_confirm = forms_board.Confirm()

    status_msg = session.get('status_msg', {"status_message": []})

    if request.method == 'GET':
        if 'status_msg' in session:
            session.pop('status_msg')

    elif request.method == 'POST':
        if form_identity.create_identity.data:
            if not form_identity.label.data or not form_identity.passphrase.data:
                status_msg['status_message'].append("Label and passphrase required")

            errors, dict_chan_info = process_passphrase(form_identity.passphrase.data)
            if dict_chan_info:
                status_msg['status_message'].append("Cannot create an Identity with board/list passphrase")

            if not status_msg['status_message']:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API, to=config.API_LOCK_TIMEOUT):
                    try:
                        b64_passphrase = base64.b64encode(form_identity.passphrase.data.encode())
                        return_str = api.createDeterministicAddresses(b64_passphrase.decode())
                        if return_str:
                            if ("addresses" in return_str and
                                    len(return_str["addresses"]) == 1 and
                                    return_str["addresses"][0]):

                                ident = Identity.query.filter(
                                    Identity.address == return_str["addresses"][0]).first()
                                if ident:
                                    logger.info(
                                        "Creating identity that already exists in the database. "
                                        "Skipping adding entry")
                                else:
                                    new_ident = Identity()
                                    new_ident.address = return_str["addresses"][0]
                                    new_ident.label = form_identity.label.data
                                    new_ident.passphrase_base64 = b64_passphrase
                                    new_ident.save()

                                daemon_com.refresh_identities()

                                if form_identity.resync.data:
                                    daemon_com.signal_clear_inventory()

                                status_msg['status_title'] = "Success"
                                status_msg['status_message'].append(
                                    "Created identity {} with address {}.".format(
                                        form_identity.label.data, return_str["addresses"][0]))
                                status_msg['status_message'].append(
                                    "Give the system a few seconds for the change to take effect.")
                            else:
                                status_msg['status_message'].append(
                                    "Error creating Identity: {}".format(return_str))
                        else:
                            status_msg['status_message'].append("Error creating Identity")
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

        elif form_identity.rename.data:
            if not form_identity.ident_label.data or not form_identity.address.data:
                status_msg['status_message'].append("Label and address required")

            if not status_msg['status_message']:
                ident = Identity.query.filter(
                    Identity.address == form_identity.address.data).first()
                if ident:
                    ident.label = form_identity.ident_label.data
                    ident.save()
                    daemon_com.refresh_identities()
                    status_msg['status_title'] = "Success"
                    status_msg['status_message'].append("Identity renamed.")
                    status_msg['status_message'].append(
                        "Give the system a few seconds for the change to take effect.")

        elif form_identity.delete.data:
            ident = None
            if not form_identity.address.data:
                status_msg['status_message'].append("Address required")
            else:
                ident = Identity.query.filter(
                    Identity.address == form_identity.address.data).first()

            if not form_confirm.confirm.data:
                return render_template("pages/confirm.html",
                                       action="delete_identity",
                                       ident=ident)

            if not status_msg['status_message']:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API, to=config.API_LOCK_TIMEOUT):
                    try:
                        return_str = api.deleteAddress(form_identity.address.data)
                        if return_str == "success":
                            if ident:
                                ident.delete()
                            daemon_com.refresh_identities()
                            status_msg['status_title'] = "Success"
                            status_msg['status_message'].append("Identity deleted.")
                            status_msg['status_message'].append(
                                "Give the system a few seconds for the change to take effect.")
                        else:
                            status_msg['status_message'].append(
                                "Error deleting Identity: {}".format(return_str))
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

        session['status_msg'] = status_msg

        if 'status_title' not in status_msg and status_msg['status_message']:
            status_msg['status_title'] = "Error"

        return redirect(url_for("routes_identities.identities"))

    return render_template("pages/identities.html",
                           form_identity=form_identity,
                           status_msg=status_msg)
コード例 #7
0
ファイル: replacements.py プロジェクト: 813492291816/BitChan
def format_body(message_id, body, truncate):
    """
    Formatting of post body text at time of page render
    Mostly to allow links to properly form after initial message processing from bitmessage
    """
    if not body:
        return ""

    split = False

    this_message = Messages.query.filter(
        Messages.message_id == message_id).first()

    lines = body.split("<br/>")

    if ((truncate and len(lines) > config.BOARD_MAX_LINES)
            or (truncate and len(body) > config.BOARD_MAX_CHARACTERS)):
        split = True

    if split:
        lines = lines[:config.BOARD_MAX_LINES]

    total_popups = 0

    regex_passphrase = r"""(\[\"(private|public)\"\,\s\"(board|list)\"\,\s\".{1,25}?\"\,\s\".{1,128}?\"\,\s\[(\"BM\-[a-zA-Z0-9]{32,34}(\"\,\s)?|\"?)*\]\,\s\[(\"BM\-[a-zA-Z0-9]{32,34}(\"\,\s)?|\"?)*\]\,\s\[(\"BM\-[a-zA-Z0-9]{32,34}(\"\,\s)?|\"?)*\]\,\s\[((\"BM\-[a-zA-Z0-9]{32,34}(\"\,\s)?|\"?)*\]\,\s\{(.*?)(\})|(\}\}))\,\s\"(.*?)\"\])"""
    regex_passphrase_base64_link = r"""http\:\/\/(172\.28\.1\.1|\blocalhost\b)\:8000\/join_base64\/([A-Za-z0-9+&]+={0,2})(\s|\Z)"""
    regex_address = r'(\[identity\](BM\-[a-zA-Z0-9]{32,34})\[\/identity\])'

    # Used to store multi-line strings to replace >>> crosspost text.
    # Needs to occur at end after all crossposts have been found.
    dict_replacements = {}

    for line in range(len(lines)):
        # Search and append identity addresses with useful links
        number_finds = len(
            re.findall(regex_address, html.unescape(lines[line])))
        for i in range(number_finds):
            each_find = re.search(regex_address, html.unescape(lines[line]))
            to_replace = each_find.groups()[0]
            address = each_find.groups()[1]

            identity = Identity.query.filter(
                Identity.address == address).first()
            address_book = AddressBook.query.filter(
                AddressBook.address == address).first()

            replaced_code = """<img style="width: 15px; height: 15px; position: relative; top: 3px;" src="/icon/{0}"> <span class="replace-funcs">{0}</span> (<button type="button" class="btn" title="Copy to Clipboard" onclick="CopyToClipboard('{0}')">&#128203;</button>""".format(
                address)

            if identity:
                replaced_code += ' <span class="replace-funcs">You, {}</span>'.format(
                    identity.label)
            elif address_book:
                replaced_code += ' <span class="replace-funcs">{},</span>' \
                                 ' <a class="link" href="/compose/0/{}">Send Message</a>'.format(
                                    address_book.label, address)
            else:
                replaced_code += ' <a class="link" href="/compose/0/{0}">Send Message</a>,' \
                                 ' <a class="link" href="/address_book_add/{0}">Add to Address Book</a>'.format(
                                    address)
            replaced_code += ')'

            lines[line] = lines[line].replace(to_replace, replaced_code, 1)

        # Search and replace Board/List Passphrase with link
        number_finds = len(
            re.findall(regex_passphrase, html.unescape(lines[line])))
        for i in range(number_finds):
            url = None
            each_find = re.search(regex_passphrase, html.unescape(lines[line]))
            passphrase = each_find.groups()[0]
            passphrase_escaped = html.escape(passphrase)

            # Find if passphrase already exists (already joined board/list)
            chan = Chan.query.filter(Chan.passphrase == passphrase).first()
            if chan:
                link_text = ""
                if chan.access == "public":
                    link_text += "Public "
                elif chan.access == "private":
                    link_text += "Private "
                if chan.type == "board":
                    link_text += "Board "
                elif chan.type == "list":
                    link_text += "List "
                link_text += "/{}/".format(chan.label)
                if chan.type == "board":
                    url = '<a class="link" href="/board/{a}/1" title="{d}">{l}</a>'.format(
                        a=chan.address, d=chan.description, l=link_text)
                elif chan.type == "list":
                    url = '<a class="link" href="/list/{a}" title="{d}">{l}</a>'.format(
                        a=chan.address, d=chan.description, l=link_text)
            else:
                errors, pass_info = process_passphrase(passphrase)
                if errors:
                    continue
                link_text = ""
                if pass_info["access"] == "public":
                    link_text += "Public "
                elif pass_info["access"] == "private":
                    link_text += "Private "
                if pass_info["type"] == "board":
                    link_text += "Board "
                elif pass_info["type"] == "list":
                    link_text += "List "
                link_text += "/{}/".format(pass_info["label"])
                url = """<a class="link" href="/join_base64/{p}" title="{d}">{l} (Click to Join)</a>""".format(
                    p=base64.b64encode(passphrase.encode()).decode(),
                    d=pass_info["description"],
                    l=link_text)
            if url:
                lines[line] = lines[line].replace(passphrase_escaped, url, 1)

        # Search and replace Board/List Passphrase base64 links with friendlier link
        number_finds = len(
            re.findall(regex_passphrase_base64_link,
                       html.unescape(lines[line])))
        for i in range(number_finds):
            url = None
            each_find = re.search(regex_passphrase_base64_link,
                                  html.unescape(lines[line]))

            link = each_find.group()
            if len(each_find.groups()) < 2:
                continue
            passphrase_encoded = each_find.groups()[1]
            passphrase_dict_json = base64.b64decode(
                passphrase_encoded.replace("&", "/")).decode()
            passphrase_dict = json.loads(passphrase_dict_json)
            passphrase_decoded = passphrase_dict["passphrase"]

            # Find if passphrase already exists (already joined board/list)
            chan = Chan.query.filter(
                Chan.passphrase == passphrase_decoded).first()
            if chan:
                link_text = ""
                if chan.access == "public":
                    link_text += "Public "
                elif chan.access == "private":
                    link_text += "Private "
                if chan.type == "board":
                    link_text += "Board "
                elif chan.type == "list":
                    link_text += "List "
                link_text += "/{}/".format(chan.label)
                if chan.type == "board":
                    url = '<a class="link" href="/board/{a}/1" title="{d}">{l}</a>'.format(
                        a=chan.address, d=chan.description, l=link_text)
                elif chan.type == "list":
                    url = '<a class="link" href="/list/{a}" title="{d}">{l}</a>'.format(
                        a=chan.address, d=chan.description, l=link_text)
            else:
                errors, pass_info = process_passphrase(passphrase_decoded)
                if errors:
                    logger.error(
                        "Errors parsing passphrase: {}".format(errors))
                    continue
                link_text = ""
                if pass_info["access"] == "public":
                    link_text += "Public "
                elif pass_info["access"] == "private":
                    link_text += "Private "
                if pass_info["type"] == "board":
                    link_text += "Board "
                elif pass_info["type"] == "list":
                    link_text += "List "
                link_text += "/{}/".format(pass_info["label"])
                url = """<a class="link" href="/join_base64/{p}" title="{d}">{l} (Click to Join)</a>""".format(
                    p=passphrase_encoded,
                    d=pass_info["description"],
                    l=link_text)
            if url:
                lines[line] = lines[line].replace(link.strip(), url, 1)

        # Search and replace BM address with post ID with link
        dict_chans_threads_strings = is_board_post_reply(lines[line])
        if dict_chans_threads_strings:
            for each_string, each_address in dict_chans_threads_strings.items(
            ):
                total_popups += lines[line].count(each_string)
                if total_popups > 50:
                    break

                board_address = each_address.split("/")[0]
                board_post_id = each_address.split("/")[1]
                chan_entry = db_return(Chan).filter(
                    and_(Chan.type == "board",
                         Chan.address == board_address)).first()
                if chan_entry:
                    message = db_return(Messages).filter(
                        Messages.post_id == board_post_id).first()
                    if message:
                        link_text = '&gt;&gt;&gt;/{l}/{p}'.format(
                            l=html.escape(chan_entry.label), p=message.post_id)
                        rep_str = get_reply_link_html(message,
                                                      external_thread=True,
                                                      external_board=True,
                                                      link_text=link_text)

                        # Store replacement in dict to conduct after all matches have been found
                        new_id = str(uuid.uuid4())
                        dict_replacements[new_id] = rep_str
                        lines[line] = lines[line].replace(each_string, new_id)

        # Search and replace only BM address with link
        dict_chans_strings = is_chan_reply(lines[line])
        if dict_chans_strings:
            for each_string, each_address in dict_chans_strings.items():
                chan_entry = db_return(Chan).filter(
                    and_(Chan.type == "board",
                         Chan.address == each_address)).first()
                list_entry = db_return(Chan).filter(
                    and_(Chan.type == "list",
                         Chan.address == each_address)).first()
                if chan_entry:
                    lines[line] = lines[line].replace(
                        each_string,
                        '<a class="link" href="/board/{a}/1" title="{d}">>>>/{l}/</a>'
                        .format(a=each_address,
                                d=chan_entry.description.replace(
                                    '"', '&quot;'),
                                l=html.escape(chan_entry.label)))
                elif list_entry:
                    lines[line] = lines[line].replace(
                        each_string,
                        '<a class="link" href="/list/{a}" title="{d}">>>>/{l}/</a>'
                        .format(a=each_address,
                                d=list_entry.description,
                                l=list_entry.label))

        # Find and replace hyperlinks
        list_links = []
        for each_word in lines[line].split(" "):
            parsed = parse.urlparse(each_word)
            if parsed.scheme and parsed.netloc:
                if "&gt;" not in parsed.geturl():
                    list_links.append(parsed.geturl())
        for each_link in list_links:
            lines[line] = lines[line].replace(
                each_link,
                '<a class="link" href="{l}" target="_blank">{l}</a>'.format(
                    l=each_link))

        # Search and replace Post Reply ID with link
        # Must come after replacement of hyperlinks
        dict_ids_strings = is_post_id_reply(lines[line])
        if dict_ids_strings:
            for each_string, targetpostdata in dict_ids_strings.items():
                total_popups += lines[line].count(each_string)
                if total_popups > 50:
                    break

                # Determine if OP or identity/address book label is to be appended to reply post ID
                message = Messages.query.filter(
                    Messages.post_id == targetpostdata["id"]).first()

                name_str = ""
                self_post = False
                if message:
                    identity = Identity.query.filter(
                        Identity.address == message.address_from).first()
                    if not name_str and identity and identity.label:
                        self_post = True
                        name_str = " ({})".format(identity.label)
                    address_book = AddressBook.query.filter(
                        AddressBook.address == message.address_from).first()
                    if not name_str and address_book and address_book.label:
                        name_str = " ({})".format(address_book.label)

                # Same-thread reference
                if (targetpostdata["location"] == "local" and message
                        and message.thread and this_message
                        and this_message.thread and message.thread.thread_hash
                        == this_message.thread.thread_hash):
                    if message.thread.op_sha256_hash == message.message_sha256_hash:
                        name_str = " (OP)"
                    rep_str = get_reply_link_html(message,
                                                  self_post=self_post,
                                                  name_str=name_str)

                # Off-board cross-post
                elif (targetpostdata["location"] == "remote" and message
                      and message.thread and this_message
                      and this_message.thread and message.thread.thread_hash !=
                      this_message.thread.thread_hash
                      and message.thread.chan.address !=
                      this_message.thread.chan.address):
                    rep_str = get_reply_link_html(message,
                                                  self_post=self_post,
                                                  name_str=name_str,
                                                  external_thread=True,
                                                  external_board=True)

                # Off-thread cross-post
                elif (targetpostdata["location"] == "remote" and message
                      and message.thread and this_message
                      and this_message.thread and message.thread.thread_hash !=
                      this_message.thread.thread_hash):
                    rep_str = get_reply_link_html(message,
                                                  self_post=self_post,
                                                  name_str=name_str,
                                                  external_thread=True)

                # No reference/cross-post found
                else:
                    rep_str = each_string

                # Store replacement in dict to conduct after all matches have been found
                new_id = str(uuid.uuid4())
                dict_replacements[new_id] = rep_str
                lines[line] = lines[line].replace(each_string, new_id)

    return_body = "<br/>".join(lines)

    for id_to_replace, replace_with in dict_replacements.items():
        return_body = return_body.replace(id_to_replace, replace_with)

    if split:
        truncate_str = '<br/><br/><span class="expand">Comment truncated. ' \
                       '<a class="link" href="/thread/{ca}/{th}#{pid}">Click here</a>' \
                       ' to view the full post.</span>'.format(
            ca=this_message.thread.chan.address,
            th=this_message.thread.thread_hash_short,
            pid=this_message.post_id)
        return_body += truncate_str

    return return_body
コード例 #8
0
def process_admin(msg_dict, msg_decrypted_dict):
    """Process message as an admin command"""
    logger.info("{}: Message is an admin command".format(
        msg_dict["msgid"][-config.ID_LENGTH:].upper()))

    # Authenticate sender
    with session_scope(DB_PATH) as new_session:
        chan = new_session.query(Chan).filter(
            Chan.address == msg_dict['toAddress']).first()
        if chan:
            errors, dict_info = process_passphrase(chan.passphrase)
            # Message must be from address in primary or secondary access list
            access = get_access(msg_dict['toAddress'])
            if errors or (msg_dict['fromAddress']
                          not in access["primary_addresses"]
                          and msg_dict['fromAddress']
                          not in access["secondary_addresses"]):
                logger.error(
                    "{}: Unauthorized Admin message. Deleting.".format(
                        msg_dict["msgid"][-config.ID_LENGTH:].upper()))
                daemon_com.trash_message(msg_dict["msgid"])
                return
        else:
            logger.error("{}: Admin message: Chan not found".format(
                msg_dict["msgid"][-config.ID_LENGTH:].upper()))
            daemon_com.trash_message(msg_dict["msgid"])
            return

    logger.info(
        "{}: Admin message received from {} for {} is authentic".format(
            msg_dict["msgid"][-config.ID_LENGTH:].upper(),
            msg_dict['fromAddress'], msg_dict['toAddress']))

    admin_dict = {
        "timestamp_utc": 0,
        "chan_type": None,
        "action": None,
        "action_type": None,
        "options": {},
        "thread_id": None,
        "message_id": None,
        "chan_address": None
    }

    if "timestamp_utc" in msg_decrypted_dict and msg_decrypted_dict[
            "timestamp_utc"]:
        admin_dict["timestamp_utc"] = msg_decrypted_dict["timestamp_utc"]
    if "chan_type" in msg_decrypted_dict and msg_decrypted_dict["chan_type"]:
        admin_dict["chan_type"] = msg_decrypted_dict["chan_type"]
    if "action" in msg_decrypted_dict and msg_decrypted_dict["action"]:
        admin_dict["action"] = msg_decrypted_dict["action"]
    if "action_type" in msg_decrypted_dict and msg_decrypted_dict[
            "action_type"]:
        admin_dict["action_type"] = msg_decrypted_dict["action_type"]
    if "options" in msg_decrypted_dict and msg_decrypted_dict["options"]:
        admin_dict["options"] = msg_decrypted_dict["options"]
    if "thread_id" in msg_decrypted_dict and msg_decrypted_dict["thread_id"]:
        admin_dict["thread_id"] = msg_decrypted_dict["thread_id"]
    if "message_id" in msg_decrypted_dict and msg_decrypted_dict["message_id"]:
        admin_dict["message_id"] = msg_decrypted_dict["message_id"]
    if "chan_address" in msg_decrypted_dict and msg_decrypted_dict[
            "chan_address"]:
        admin_dict["chan_address"] = msg_decrypted_dict["chan_address"]

    access = get_access(msg_dict['toAddress'])

    lf = LF()
    if lf.lock_acquire(config.LOCKFILE_ADMIN_CMD, to=20):
        try:
            # (Owner): set board options
            if (admin_dict["action"] == "set"
                    and admin_dict["action_type"] == "options" and
                    msg_dict['fromAddress'] in access["primary_addresses"]):
                admin_set_options(msg_dict, admin_dict)

            # (Owner, Admin): set thread options
            elif (
                    admin_dict["action"] == "set"
                    and admin_dict["action_type"] == "thread_options" and
                (msg_dict['fromAddress'] in access["primary_addresses"]
                 or msg_dict['fromAddress'] in access["secondary_addresses"])):
                admin_set_thread_options(msg_dict, admin_dict)

            # (Owner, Admin): delete board thread or post
            elif (
                    admin_dict["action"] == "delete"
                    and admin_dict["chan_type"] == "board" and
                (msg_dict['fromAddress'] in access["primary_addresses"]
                 or msg_dict['fromAddress'] in access["secondary_addresses"])):
                admin_delete_from_board(msg_dict, admin_dict)

            # (Owner, Admin): delete board post with comment
            elif (
                    admin_dict["action"] == "delete_comment"
                    and admin_dict["action_type"] == "post"
                    and "options" in admin_dict
                    and "delete_comment" in admin_dict["options"]
                    and "message_id" in admin_dict["options"]["delete_comment"]
                    and "comment" in admin_dict["options"]["delete_comment"]
                    and
                (msg_dict['fromAddress'] in access["primary_addresses"]
                 or msg_dict['fromAddress'] in access["secondary_addresses"])):
                admin_delete_from_board_with_comment(msg_dict, admin_dict)

            # (Owner, Admin): Ban user
            elif (
                    admin_dict["action"]
                    in ["board_ban_silent", "board_ban_public"]
                    and admin_dict["action_type"] in "ban_address"
                    and admin_dict["options"]
                    and "ban_address" in admin_dict["action_type"] and
                (msg_dict['fromAddress'] in access["primary_addresses"]
                 or msg_dict['fromAddress'] in access["secondary_addresses"])):
                admin_ban_address_from_board(msg_dict, admin_dict)

            else:
                logger.error("{}: Unknown Admin command. Deleting. {}".format(
                    msg_dict["msgid"][-config.ID_LENGTH:].upper(), admin_dict))
                daemon_com.trash_message(msg_dict["msgid"])
        except Exception:
            logger.exception(
                "{}: Exception processing Admin command. Deleting.".format(
                    msg_dict["msgid"][-config.ID_LENGTH:].upper()))
            daemon_com.trash_message(msg_dict["msgid"])
        finally:
            time.sleep(config.API_PAUSE)
            lf.lock_release(config.LOCKFILE_API)