Exemple #1
0
def images(message_id, file_type, filename):
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    filename = unquote(filename)

    post = Messages.query.filter(Messages.message_id == message_id).first()
    if not post:
        if file_type == "god_song":
            pass  # God songs can be in the long description
        else:
            return "Message ID not found"

    if file_type == "thumb":
        # Return image thumbnail
        file_order, media_info, _ = attachment_info(message_id)
        path = "{}/{}_thumb".format(config.FILE_DIRECTORY, message_id)
        if filename in media_info:
            return get_image_attach(
                message_id, path, filename, media_info[filename]["extension"])
    if file_type == "thumb_first":
        # Return image thumbnail
        file_order, media_info, _ = attachment_info(message_id)
        path = "{}/{}_thumb".format(config.FILE_DIRECTORY, message_id)
        for filename in media_info:
            if media_info[filename]["extension"] in config.FILE_EXTENSIONS_IMAGE:
                if media_info[filename]['spoiler']:
                    return send_file("/home/bitchan/static/spoiler.png", mimetype="image/png")
                else:
                    return get_image_attach(
                        message_id, path, filename, media_info[filename]["extension"])
    elif file_type == "image":
        # Return image file
        file_order, media_info, _ = attachment_info(message_id)
        path = "{}/{}".format(config.FILE_DIRECTORY, message_id)
        if filename in media_info:
            return get_image_attach(
                message_id, path, filename, media_info[filename]["extension"])
    elif file_type == "god_song":
        file_path = "{}/{}_god_song.mp3".format(config.FILE_DIRECTORY, message_id)
        if os.path.exists(file_path):
            return send_file(file_path, mimetype="audio/mp3")
        else:
            return "Could not find God Song file at {}".format(file_path)
    elif file_type == "file":
        # Return potentially non-image file
        file_order, media_info, _ = attachment_info(message_id)
        path = "{}/{}".format(config.FILE_DIRECTORY, message_id)
        file_path = "{}/{}".format(path, filename)
        if os.path.exists(file_path):
            if (filename in media_info and
                    media_info[filename]["extension"] in config.FILE_EXTENSIONS_IMAGE):
                return get_image_attach(
                    message_id, path, filename, media_info[filename]["extension"])
            else:
                return send_file(file_path, attachment_filename=filename)
    else:
        logger.error("File '{}' not found for message {}".format(filename, message_id))
        return ""
Exemple #2
0
def spoiler_image(chan_address):
    """Returns a spoiler image based on whether a custom banner is available"""
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    chan = Chan.query.filter(Chan.address == chan_address).first()

    if chan:
        admin_cmd = Command.query.filter(and_(
            Command.chan_address == chan.address,
            Command.action == "set",
            Command.action_type == "options")).first()
        if admin_cmd:
            try:
                options = json.loads(admin_cmd.options)
            except:
                options = {}
            if "spoiler_base64" in options and options["spoiler_base64"]:
                return send_file(
                    BytesIO(base64.b64decode(options["spoiler_base64"])),
                    mimetype='image/png',
                    cache_timeout=1440)

    file_path = "/home/bitchan/static/spoiler.png"
    return send_file(file_path, mimetype='image/png', cache_timeout=1440)
Exemple #3
0
def icon_image(address):
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    path_icon = os.path.join(config.FILE_DIRECTORY, "{}_icon.png".format(address))
    if not os.path.exists(path_icon):
        generate_icon(address)
    return send_file(path_icon, mimetype='image/png', cache_timeout=1440)
Exemple #4
0
def download(message_id, filename):
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    post = Messages.query.filter(Messages.message_id == message_id).first()
    file_path = "{}/{}/{}".format(config.FILE_DIRECTORY, message_id, filename)
    if post and os.path.exists(file_path):
        return send_file(file_path,
                         attachment_filename=filename,
                         as_attachment=True)
def verify_wait():
    allowed, allow_msg = allowed_access(check_can_view=True)
    if not allowed:
        return allow_msg

    if "session_id" not in session:
        session["session_id"] = str(uuid.uuid4())

    page_id = str(uuid.uuid4())
    session[page_id] = time.time()

    return render_template("pages/verify_wait.html", page_id=page_id)
Exemple #6
0
def custom_flag_by_post_id(post_id):
    """Returns a flag image based on the post ID"""
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    message = Messages.query.filter(Messages.message_id == post_id).first()

    if message:
        return send_file(
            BytesIO(base64.b64decode(message.nation_base64)),
            mimetype='image/jpg',
            cache_timeout=1440)
Exemple #7
0
def custom_flag_by_flag_id(flag_id):
    """Returns a flag image based on the flag ID"""
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    flag = Flags.query.filter(Flags.id == int(flag_id)).first()

    if flag:
        return send_file(
            BytesIO(base64.b64decode(flag.flag_base64)),
            mimetype='image/{}'.format(flag.flag_extension),
            cache_timeout=1440)
def verify_test(page_id):
    allowed, allow_msg = allowed_access(check_can_view=True)
    if not allowed:
        return allow_msg

    if page_id not in session or "session_id" not in session:
        return '<div style="text-align:center;padding-top:2em">Invalid ID. <a href="/">Reverify</a></div>'

    ts = session[page_id]
    if time.time() < ts + 5:
        session.pop(page_id)
        return '<div style="text-align:center;padding-top:2em">Invalid Wait. <a href="/">Reverify</a></div>'

    if request.method == 'POST':
        page_id = request.form.get('page_id', None)
        if captcha.validate(page_id=page_id):
            session_test = SessionInfo.query.filter(
                SessionInfo.session_id == session["session_id"]).first()
            if not session_test:
                session_info = SessionInfo()
                session_info.session_id = session["session_id"]
                session_info.request_rate_ts = time.time()
                session_info.request_rate_amt = 0
                session_info.verified = True
                session_info.save()
            else:
                session_test.session_id = session["session_id"]
                session_test.request_rate_ts = time.time()
                session_test.request_rate_amt = 0
                session_test.verified = True
                session_test.save()
            session["verified"] = True
            session.pop(page_id)
            logger.info("Post request session: {}".format(session))
            return redirect(url_for('routes_main.index'))
        else:
            if "verify_captcha_count" not in session:
                session["verify_captcha_count"] = 1
            elif session["verify_captcha_count"] > 4:
                session["verify_captcha_count"] = 0
                session.pop(page_id)
                return redirect(url_for('routes_verify.verify_wait'))
            else:
                session["verify_captcha_count"] += 1

    return render_template("pages/verify_test.html", page_id=page_id)
Exemple #9
0
def banner_image(chan_address):
    """Returns a banner image based on whether a custom banner is available"""
    file_path = None
    settings = GlobalSettings.query.first()

    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        if settings.theme in config.THEMES_DARK:
            file_path = "/home/bitchan/static/banner_dark.png"
        elif settings.theme in config.THEMES_LIGHT:
            file_path = "/home/bitchan/static/banner_light.png"
        if file_path:
            return send_file(file_path, mimetype='image/png', cache_timeout=1440)
        else:
            return allow_msg

    chan = Chan.query.filter(Chan.address == chan_address).first()

    if chan:
        admin_cmd = Command.query.filter(and_(
            Command.chan_address == chan.address,
            Command.action == "set",
            Command.action_type == "options")).first()
        if admin_cmd:
            try:
                options = json.loads(admin_cmd.options)
            except:
                options = {}
            if "banner_base64" in options and options["banner_base64"]:
                return send_file(
                    BytesIO(base64.b64decode(options["banner_base64"])),
                    mimetype='image/png',
                    cache_timeout=1440)

    if settings.theme in config.THEMES_DARK:
        file_path = "/home/bitchan/static/banner_dark.png"
    elif settings.theme in config.THEMES_LIGHT:
        file_path = "/home/bitchan/static/banner_light.png"

    if file_path:
        return send_file(file_path, mimetype='image/png', cache_timeout=1440)
    return "Error determining the banner image to use"
def login_info():
    allowed, allow_msg = allowed_access()
    if not allowed:
        return allow_msg

    status_msg = {"status_message": []}
    board = {"current_chan": None}

    if ('uuid' in session and session['uuid'] in flask_session_login
            and 'logged_in' in flask_session_login[session['uuid']]
            and flask_session_login[session['uuid']]['logged_in']
            and 'credentials' in flask_session_login[session['uuid']]):
        login_credentials = flask_session_login[session['uuid']]['credentials']
        login_credentials['uuid'] = session['uuid']
    else:
        login_credentials = None

    return render_template("pages/login_info.html",
                           board=board,
                           login_credentials=login_credentials,
                           status_msg=status_msg)
def leave(address):
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_confirm = forms_board.Confirm()

    chan = Chan.query.filter(Chan.address == address).first()

    if request.method != 'POST' or not form_confirm.confirm.data:
        return render_template("pages/confirm.html",
                               action="leave",
                               address=address,
                               chan=chan)

    status_msg = {"status_message": []}

    admin_cmds = Command.query.filter(Command.chan_address == address).all()
    for each_adm_cmd in admin_cmds:
        each_adm_cmd.delete()

    lf = LF()
    if lf.lock_acquire(config.LOCKFILE_MSG_PROC, to=60):
        try:
            for each_thread in chan.threads:
                for each_message in each_thread.messages:
                    delete_post(each_message.message_id)  # Delete thread posts
                delete_thread(each_thread.thread_hash)  # Delete thread

            deleted_msgs = DeletedMessages.query.filter(
                DeletedMessages.address_to == address).all()
            for each_msg in deleted_msgs:
                logger.info("DeletedMessages: Deleting entry: {}".format(
                    each_msg.message_id))
                each_msg.delete()

            try:
                daemon_com.leave_chan(address)  # Leave chan in Bitmessage
                delete_chan(address)  # Delete chan

                # Delete mod log entries for address
                mod_logs = ModLog.query.filter(
                    ModLog.board_address == address).all()
                for each_entry in mod_logs:
                    each_entry.delete()
            except:
                logger.exception(
                    "Could not delete chan via daemon or delete_chan()")

            daemon_com.delete_and_vacuum()

            status_msg['status_title'] = "Success"
            status_msg['status_message'].append("Deleted {}".format(address))
        finally:
            time.sleep(1)
            lf.lock_release(config.LOCKFILE_MSG_PROC)

    board = {"current_chan": None}
    url = ""
    url_text = ""

    return render_template("pages/alert.html",
                           board=board,
                           status_msg=status_msg,
                           url=url,
                           url_text=url_text)
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)
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)
Exemple #14
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)
Exemple #15
0
def diag():
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

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

    # get all messages sending
    import sqlite3
    from binascii import hexlify
    row = []
    try:
        conn = sqlite3.connect('file:{}'.format(config.messages_dat), uri=True)
        conn.text_factory = bytes
        c = conn.cursor()
        c.execute(
            "SELECT msgid, fromaddress, toaddress, lastactiontime, message, status "
            "FROM sent "
            "WHERE folder='sent'")
        row = c.fetchall()
        conn.commit()
        conn.close()
    except Exception as err:
        logger.exception("Error checking for POW: {}".format(err))

    # Convert msg IDs
    sending_msgs = []
    for each_row in row:
        if each_row[5].decode() in ["doingmsgpow", "msgqueued"]:
            sending_msgs.append(
                (hexlify(each_row[0]).decode(),
                 each_row[1].decode(), each_row[2].decode(), each_row[3],
                 len(each_row[4]), each_row[5].decode()))

    if request.method == 'POST':
        if form_diag.del_sending_msg.data:
            cancel_send_id_list = []
            for each_input in request.form:
                if each_input.startswith("delsendingmsgid_"):
                    cancel_send_id_list.append(each_input.split("_")[1])

            if not cancel_send_id_list:
                status_msg['status_message'].append(
                    "Must select at least one message to cancel the sending of."
                )

            if not status_msg['status_message']:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API,
                                   to=config.API_LOCK_TIMEOUT):
                    try:
                        for each_id in cancel_send_id_list:
                            logger.info(
                                "Trashing msg with ID: {}".format(each_id))
                            api.trashSentMessage(each_id)
                            time.sleep(0.1)

                        time.sleep(1)
                        daemon_com.restart_bitmessage()
                        status_msg['status_title'] = "Success"
                        status_msg['status_message'].append(
                            "Deleted message(s) being sent and restarting Bitmessage. "
                            "Please wait at least 60 seconds before canceling another send."
                        )
                    except Exception as err:
                        logger.error("Error: {}".format(err))
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

        if form_diag.del_inventory.data:
            try:
                daemon_com.clear_bm_inventory()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Deleted Bitmessage inventory and restarting Bitmessage. Give it time to resync."
                )
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete Bitmessage inventory: {}".format(err))
                logger.exception("Couldn't delete BM inventory")

        elif form_diag.del_deleted_msg_db.data:
            try:
                deleted_msgs = DeletedMessages.query.all()
                for each_msg in deleted_msgs:
                    logger.info("DeletedMessages: Deleting entry: {}".format(
                        each_msg.message_id))
                    each_msg.delete()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Cleared Deleted Message table")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't clear Deleted Message table: {}".format(err))
                logger.exception("Couldn't clear Deleted Message table")

        elif form_diag.del_non_bc_msg_list.data:
            try:
                settings = GlobalSettings.query.first()
                settings.discard_message_ids = "[]"
                settings.save()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Cleared Non-BC Message List")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't clear Non-BC Message List: {}".format(err))
                logger.exception("Couldn't clear Non-BC Message List")

        elif form_diag.del_trash.data:
            try:
                daemon_com.delete_and_vacuum()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Deleted Bitmessage Trash items.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete Bitmessage Trash items: {}".format(err))
                logger.exception("Couldn't delete BM Trash Items")

        elif form_diag.del_popup_html.data:
            try:
                for each_message in Messages.query.all():
                    each_message.popup_html = ""
                    each_message.save()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Deleted popup HTML for all messages.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete popup HTML: {}".format(err))
                logger.exception("Couldn't delete popup HTML")

        elif form_diag.del_cards.data:
            try:
                cards = PostCards.query.all()
                for each_card in cards:
                    each_card.delete()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Deleted cards.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete cards: {}".format(err))
                logger.exception("Couldn't delete cards")

        elif form_diag.del_mod_log.data:
            try:
                mod_logs = ModLog.query.all()
                for each_entry in mod_logs:
                    each_entry.delete()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Deleted Mod Log.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete Mod Log: {}".format(err))
                logger.exception("Couldn't delete Mod Log")

        elif form_diag.del_posts_without_thread.data:
            try:
                messages = Messages.query.all()
                for each_msg in messages:
                    if not each_msg.thread:
                        each_msg.delete()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Deleted orphaned posts.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't delete orphaned posts: {}".format(err))
                logger.exception("Couldn't delete orphaned posts")

        elif form_diag.fix_thread_board_timestamps.data:
            try:
                threads = Threads.query.all()
                for each_thread in threads:
                    latest_post = Messages.query.filter(
                        Messages.thread_id == each_thread.id).order_by(
                            Messages.timestamp_sent.desc()).first()
                    if latest_post:
                        each_thread.timestamp_sent = latest_post.timestamp_sent
                        each_thread.save()

                boards = Chan.query.filter(Chan.type == "board").all()
                for each_board in boards:
                    latest_thread = Threads.query.filter(
                        Threads.chan_id == each_board.id).order_by(
                            Threads.timestamp_sent.desc()).first()
                    if latest_thread:
                        each_board.timestamp_sent = latest_thread.timestamp_sent
                        each_board.save()

                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Fixed thread and board timestamps.")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't fix thread and board timestamps: {}".format(err))
                logger.exception("Couldn't fix thread and board timestamps")

        elif form_diag.fix_thread_short_hashes.data:
            try:
                threads = Threads.query.all()
                for each_thread in threads:
                    each_thread.thread_hash_short = each_thread.thread_hash[
                        -12:]
                    each_thread.save()

                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Fixed thread short hashes")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't fix thread short hashes: {}".format(err))
                logger.exception("Couldn't fix thread short hashes")

        elif form_diag.download_backup.data:
            date_now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
            filename = 'bitchan-backup_{}.tar'.format(date_now)
            save_path = '/home/{}'.format(filename)

            def delete_backup_files():
                time.sleep(7200)
                delete_files = glob.glob("/home/*.tar")
                delete_files.append(
                    '/home/bitchan/bitchan_backup-usr_bitchan.tar')
                delete_files.append(
                    '/home/bitchan/bitchan_backup-usr_bitmessage.tar')
                for each_file in delete_files:
                    delete_file(each_file)

            try:
                cmd = 'tar -cvf /home/bitchan/bitchan_backup-usr_bitchan.tar /usr/local/bitchan'
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                cmd = 'tar -cvf /home/bitchan/bitchan_backup-usr_bitmessage.tar /usr/local/bitmessage'
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                cmd = 'tar -cvf {} /home/bitchan'.format(save_path)
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                thread_download = Thread(target=delete_backup_files)
                thread_download.start()

                return send_file(save_path, mimetype='application/x-tar')
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't generate backup archive: {}".format(err))
                logger.exception("Couldn't generate backup archive")

        elif form_diag.download_backup.data:
            try:
                save_path = '/tmp/bitchan-backup_to_restore.tar'
                delete_file(save_path)
                form_diag.restore_backup_file.data.save(save_path)

                cmd = 'tar -xvf {} -C /'.format(save_path)
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                cmd = 'tar -xvf /home/bitchan/bitchan_backup-usr_bitchan.tar -C /'
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                cmd = 'tar -xvf /home/bitchan/bitchan_backup-usr_bitmessage.tar -C /'
                output = subprocess.check_output(cmd, shell=True, text=True)
                logger.debug("Command: {}, Output: {}".format(cmd, output))

                def delete_backup_files():
                    delete_files = [
                        save_path,
                        '/home/bitchan/bitchan_backup-usr_bitchan.tar',
                        '/home/bitchan/bitchan_backup-usr_bitmessage.tar'
                    ]
                    for each_file in delete_files:
                        delete_file(each_file)

                subprocess.Popen('docker stop -t 15 bitchan_daemon 2>&1',
                                 shell=True)
                time.sleep(15)
                subprocess.Popen('docker start bitchan_daemon 2>&1',
                                 shell=True)

                subprocess.Popen('docker stop -t 15 bitmessage 2>&1',
                                 shell=True)
                time.sleep(15)
                subprocess.Popen('docker start bitmessage 2>&1', shell=True)

                thread_download = Thread(target=delete_backup_files)
                thread_download.start()

                status_msg['status_title'] = "Success"
                status_msg['status_message'].append(
                    "Restored backup and restarted Bitmessage and BitChan")
            except Exception as err:
                status_msg['status_message'].append(
                    "Couldn't restore backup: {}".format(err))
                logger.exception("Couldn't restore backup archive")

        elif form_diag.bulk_delete_threads_submit.data:
            address = "0"
            if form_diag.bulk_delete_threads_address.data:
                board = Chan.query.filter(
                    Chan.address == form_diag.bulk_delete_threads_address.data)
                if not board.count():
                    status_msg['status_message'].append(
                        "Invalid Address: {}".format(
                            form_diag.bulk_delete_threads_address.data))
                else:
                    address = board.address

            return redirect(
                url_for("routes_admin.bulk_delete_thread",
                        current_chan=address))

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

    return render_template("pages/diag.html",
                           flask_session_login=flask_session_login,
                           form_diag=form_diag,
                           replace_lt_gt=replace_lt_gt,
                           sending_msgs=sending_msgs,
                           settings=GlobalSettings.query.first(),
                           status_msg=status_msg,
                           themes=themes.themes)
Exemple #16
0
def bug_report():
    allowed, allow_msg = allowed_access(check_can_view=True)
    if not allowed:
        return allow_msg

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

    if request.method == 'POST':
        if form_bug.send.data and form_bug.bug_report.data:
            try:
                # Only send from a board or list
                # Do not send from an identity
                if config.DEFAULT_CHANS[0][
                        "address"] in daemon_com.get_all_chans():
                    address_from = config.DEFAULT_CHANS[0]["address"]
                elif daemon_com.get_all_chans():
                    address_from = list(daemon_com.get_all_chans().keys())[0]
                else:
                    status_msg['status_message'].append(
                        "Could not find address to send from. "
                        "Join/Create a board or list and try again.")
                    address_from = None

                alembic_version = Alembic.query.first().version_num
                message_compiled = "BitChan version: {}\n".format(
                    config.VERSION_BITCHAN)
                message_compiled += "Database version: {} (should be {})\n\n".format(
                    alembic_version, config.VERSION_ALEMBIC)
                message_compiled += "Message:\n\n{}".format(
                    form_bug.bug_report.data)
                message_b64 = base64.b64encode(
                    message_compiled.encode()).decode()

                ts = datetime.datetime.fromtimestamp(
                    daemon_com.get_utc()).strftime('%Y-%m-%d %H:%M:%S')
                subject = "Bug Report {} ({})".format(config.VERSION_BITCHAN,
                                                      ts)
                subject_b64 = base64.b64encode(subject.encode()).decode()

                if not status_msg['status_message']:
                    if address_from:
                        # Don't allow a message to send while Bitmessage is restarting
                        allow_send = False
                        timer = time.time()
                        while not allow_send:
                            if daemon_com.bitmessage_restarting() is False:
                                allow_send = True
                            if time.time() - timer > config.BM_WAIT_DELAY:
                                logger.error(
                                    "Unable to send message: "
                                    "Could not detect Bitmessage running.")
                                return
                            time.sleep(1)

                        if allow_send:
                            lf = LF()
                            if lf.lock_acquire(config.LOCKFILE_API,
                                               to=config.API_LOCK_TIMEOUT):
                                try:
                                    return_str = api.sendMessage(
                                        config.BITCHAN_BUG_REPORT_ADDRESS,
                                        address_from, subject_b64, message_b64,
                                        2, config.BM_TTL)
                                    if return_str:
                                        status_msg['status_title'] = "Success"
                                        status_msg['status_message'].append(
                                            "Sent. Thank you for your feedback. "
                                            "Send returned: {}".format(
                                                return_str))
                                finally:
                                    time.sleep(config.API_PAUSE)
                                    lf.lock_release(config.LOCKFILE_API)

            except Exception as err:
                status_msg['status_message'].append(
                    "Could not send: {}".format(err))
                logger.exception("Could not send bug report: {}".format(err))

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

    return render_template("pages/bug_report.html",
                           form_bug=form_bug,
                           replace_lt_gt=replace_lt_gt,
                           settings=GlobalSettings.query.first(),
                           status_msg=status_msg,
                           themes=themes.themes)
Exemple #17
0
def compose(address_from, address_to):
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    from_all = []

    form_msg = forms_mailbox.Compose()

    if address_from == "0":
        address_from = ""

    if address_to == "0":
        address_to = ""

    from_all.extend(daemon_com.get_identities().keys())
    from_all.extend(daemon_com.get_all_chans().keys())

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

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

    if request.method == 'POST':
        if form_msg.send.data:
            if not form_msg.to_address.data:
                status_msg['status_message'].append(
                    "Must provide a To Address")
            if not form_msg.from_address.data:
                status_msg['status_message'].append(
                    "Must provide a From Address")
            if not (3600 <= form_msg.ttl.data <= 2419200):
                status_msg['status_message'].append(
                    "TTL must be between 3600 and 2419200")

            if not status_msg['status_message']:
                if form_msg.subject.data:
                    subject = base64.b64encode(
                        form_msg.subject.data.encode()).decode()
                else:
                    subject = ""
                if form_msg.body.data:
                    message = base64.b64encode(
                        form_msg.body.data.encode()).decode()
                else:
                    message = ""

                # Don't allow a message to send while Bitmessage is restarting
                allow_send = False
                timer = time.time()
                while not allow_send:
                    if daemon_com.bitmessage_restarting() is False:
                        allow_send = True
                    if time.time() - timer > config.BM_WAIT_DELAY:
                        logger.error("Unable to send message: "
                                     "Could not detect Bitmessage running.")
                        return
                    time.sleep(1)

                if allow_send:
                    lf = LF()
                    if lf.lock_acquire(config.LOCKFILE_API,
                                       to=config.API_LOCK_TIMEOUT):
                        try:  # TODO: message sends but results in error. Diagnose.
                            status_msg['status_title'] = "Success"
                            status_msg['status_message'].append(
                                "Message sent to queue")
                            try:
                                return_str = api.sendMessage(
                                    form_msg.to_address.data,
                                    form_msg.from_address.data, subject,
                                    message, 2, form_msg.ttl.data)
                            except Exception as err:
                                if err.__str__(
                                ) == "<Fault 21: 'Unexpected API Failure - too many values to unpack'>":
                                    return_str = "Error: API Failure (despite this error, the message probably still sent)"
                                else:
                                    return_str = "Error: {}".format(err)
                            if return_str:
                                logger.info(
                                    "Send message from {} to {}. Returned: {}".
                                    format(form_msg.from_address.data,
                                           form_msg.to_address.data,
                                           return_str))
                                status_msg['status_message'].append(
                                    "Bitmessage returned: {}".format(
                                        return_str))
                        except Exception as err:
                            logger.exception("Error: {}".format(err))
                        finally:
                            time.sleep(config.API_PAUSE)
                            lf.lock_release(config.LOCKFILE_API)

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

            form_populate = {
                "to_address": form_msg.to_address.data,
                "from_address": form_msg.from_address.data,
                "ttl": form_msg.ttl.data,
                "subject": form_msg.subject.data,
                "body": form_msg.body.data,
            }

        session['form_populate'] = form_populate
        session['status_msg'] = status_msg

        if not address_from:
            address_from = "0"

        return redirect(
            url_for("routes_mail.compose",
                    address_from=address_from,
                    address_to="0"))

    return render_template("mailbox/compose.html",
                           address_from=address_from,
                           address_to=address_to,
                           form_populate=form_populate,
                           from_all=from_all,
                           get_from_list_all=get_from_list_all,
                           status_msg=status_msg)
Exemple #18
0
def thread_steg(current_chan, thread_id):
    can_view, allow_msg = allowed_access(check_can_view=True)
    if not can_view:
        return allow_msg

    form_post = forms_board.Post()
    form_steg = forms_board.Steg()

    settings = GlobalSettings.query.first()
    chan = Chan.query.filter(Chan.address == current_chan).first()
    
    if len(thread_id) == 12:
        thread = Threads.query.filter(Threads.thread_hash_short == thread_id).first()
    else:
        thread = Threads.query.filter(Threads.thread_hash == thread_id).first()

    if not chan:
        return render_template("pages/404-board.html",
                               board_address=current_chan)

    if not thread:
        return render_template("pages/404-thread.html",
                               board_address=current_chan,
                               thread_id=thread_id)

    board = {
        "current_chan": chan,
        "current_thread": thread,
        "messages": Messages
    }
    form_populate = session.get('form_populate', {})
    status_msg = session.get('status_msg', {"status_message": []})

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

    elif request.method == 'POST':
        if form_post.start_download.data:
            can_download, allow_msg = allowed_access(check_can_download=True)
            board_list_admin, allow_msg = allowed_access(
                check_is_board_list_admin=True, check_admin_board=current_chan)
            if not can_download and not board_list_admin:
                return allow_msg

            daemon_com.set_start_download(form_post.message_id.data)
            status_msg['status_title'] = "Success"
            status_msg['status_message'].append(
                "File download initialized in the background. Give it time to download.")

        elif form_post.submit.data:
            invalid_post = False
            if not form_post.validate():
                for field, errors in form_post.errors.items():
                    if field == "csrf_token":
                        invalid_post = True
                    for error in errors:
                        logger.error("Error in the {} field - {}".format(
                            getattr(form_post, field).label.text, error))

            if invalid_post:
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid")

            if not form_post.page_id.data:
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid ID")
            elif settings.enable_captcha and not captcha.validate(page_id=form_post.page_id.data):
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid Captcha")

            can_post, allow_msg = allowed_access(check_can_post=True)
            board_list_admin, allow_msg = allowed_access(
                check_is_board_list_admin=True, check_admin_board=current_chan)
            if not can_post and not board_list_admin:
                return allow_msg

            if form_post.default_from_address.data:
                thread.default_from_address = form_post.from_address.data
            else:
                thread.default_from_address = None
            thread.save()

            status_msg, result, form_populate = post_message(
                form_post, form_steg, status_msg)

        session['form_populate'] = form_populate
        session['status_msg'] = status_msg

        return redirect(url_for("routes_board.thread_steg",
                                current_chan=current_chan,
                                thread_id=thread_id))

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

    return render_template("pages/thread_steg.html",
                           board=board,
                           form_populate=form_populate,
                           form_post=form_post,
                           form_steg=form_steg,
                           from_list=from_list,
                           page_id=str(uuid.uuid4()),
                           status_msg=status_msg,
                           upload_sites=UploadSites)
Exemple #19
0
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)
Exemple #20
0
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)
Exemple #21
0
def block_address(chan_address, block_address, block_type):
    """Block address locally, on single board or across all boards"""
    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=chan_address)
    if not global_admin and not board_list_admin:
        return allow_msg

    form_confirm = forms_board.Confirm()
    chan = Chan.query.filter(Chan.address == chan_address).first()

    board = {
        "current_chan": chan,
        "current_thread": None,
    }
    status_msg = {"status_message": []}

    if block_address in daemon_com.get_identities():
        status_msg['status_message'].append("You cannot block your own identity")
        status_msg['status_title'] = "Error"

    elif request.method != 'POST' or not form_confirm.confirm.data:
        return render_template("pages/confirm.html",
                               action="block_address",
                               block_type=block_type,
                               chan=chan,
                               chan_address=chan_address,
                               block_address=block_address)

    elif request.method == 'POST' and form_confirm.confirm.data:
        messages = Messages.query.filter(
            Messages.address_from == block_address).all()

        list_delete_message_ids = []

        for message in messages:
            if block_type == "single_board" and message.thread.chan.address == chan_address:
                list_delete_message_ids.append(message.message_id)
            elif block_type == "global":
                if not global_admin:
                    return allow_msg
                list_delete_message_ids.append(message.message_id)

        lf = LF()
        if lf.lock_acquire(config.LOCKFILE_MSG_PROC, to=60):
            try:
                # First, delete messages from database
                if list_delete_message_ids:
                    for each_id in list_delete_message_ids:
                        delete_post(each_id)
                    daemon_com.signal_generate_post_numbers()

                # Allow messages to be deleted in bitmessage before allowing bitchan to rescan inbox
                time.sleep(1)
            except Exception as err:
                logger.error("Exception while deleting messages: {}".format(err))
            finally:
                lf.lock_release(config.LOCKFILE_MSG_PROC)

        new_cmd = Command()
        new_cmd.do_not_send = True
        new_cmd.action = "block"
        new_cmd.action_type = "block_address"
        new_cmd.options = json.dumps({"block_address": block_address})
        if block_type == "single_board":
            new_cmd.chan_address = chan_address
        elif block_type == "global":
            new_cmd.chan_address = "all"  # global block (all boards)
        new_cmd.save()

        status_msg['status_title'] = "Success"
        status_msg['status_message'].append("Blocked address {}".format(block_address))

    return render_template("pages/alert.html",
                           board=board,
                           status_msg=status_msg)
Exemple #22
0
def board(current_chan, current_page):
    allowed, allow_msg = allowed_access(check_can_view=True)
    if not allowed:
        return allow_msg

    form_post = forms_board.Post()
    form_steg = forms_board.Steg()
    form_set = forms_board.SetChan()

    settings = GlobalSettings.query.first()
    chan = Chan.query.filter(Chan.address == current_chan).first()

    if not chan:
        return render_template("pages/404-board.html",
                               board_address=current_chan)

    board = {
        "current_page": int(current_page),
        "current_chan": chan,
        "current_thread": None,
        "messages": Messages,
        "threads": Threads.query
            .filter(Threads.chan_id == chan.id)
            .order_by(Threads.timestamp_sent.desc())
    }

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

    def get_threads_from_page(address, page):
        threads_sticky = []
        stickied_hash_ids = []
        thread_start = int((int(page) - 1) * settings.results_per_page_board)
        thread_end = int(int(page) * settings.results_per_page_board) - 1
        chan_ = Chan.query.filter(Chan.address == address).first()

        # Find all threads remotely stickied
        admin_cmds = Command.query.filter(
                Command.chan_address == address,
                Command.action_type == "thread_options"
            ).order_by(Command.timestamp_utc.desc()).all()
        for each_adm in admin_cmds:
            try:
                options = json.loads(each_adm.options)
            except:
                options = {}
            if "sticky" in options and options["sticky"]:
                sticky_thread = Threads.query.filter(
                    Threads.thread_hash == each_adm.thread_id).first()
                if sticky_thread:
                    stickied_hash_ids.append(sticky_thread.thread_hash)
                    threads_sticky.append(sticky_thread)

        # Find all thread locally stickied (and prevent duplicates)
        threads_sticky_db = Threads.query.filter(
            and_(
                Threads.chan_id == chan_.id,
                or_(Threads.stickied_local.is_(True))
            )).order_by(Threads.timestamp_sent.desc()).all()
        for each_db_sticky in threads_sticky_db:
            if each_db_sticky.thread_hash not in stickied_hash_ids:
                threads_sticky.append(each_db_sticky)

        threads_all = Threads.query.filter(
            and_(
                Threads.chan_id == chan_.id,
                Threads.stickied_local.is_(False)
            )).order_by(Threads.timestamp_sent.desc()).all()

        threads = []
        threads_count = 0
        for each_thread in threads_sticky:
            if threads_count > thread_end:
                break
            if thread_start <= threads_count:
                threads.append(each_thread)
            threads_count += 1

        for each_thread in threads_all:
            if each_thread.thread_hash in stickied_hash_ids:
                continue  # skip stickied threads
            if threads_count > thread_end:
                break
            if thread_start <= threads_count:
                threads.append(each_thread)
            threads_count += 1

        return threads

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

    elif request.method == 'POST':
        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 Message PGP Passphrase.")

        if form_set.set_pgp_passphrase_attach.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_attach.data:
                status_msg['status_message'].append("Attachment PGP passphrase required")
            elif len(form_set.pgp_passphrase_attach.data) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append("Attachment PGP passphrase longer than {}: {}".format(
                    config.PGP_PASSPHRASE_LENGTH, len(form_set.pgp_passphrase_attach.data)))
            else:
                chan.pgp_passphrase_attach = form_set.pgp_passphrase_attach.data
                chan.save()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Changed Attachment PGP Passphrase.")

        elif form_set.set_pgp_passphrase_steg.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_steg.data:
                status_msg['status_message'].append("Steg PGP passphrase required")
            elif len(form_set.pgp_passphrase_steg.data) > config.PGP_PASSPHRASE_LENGTH:
                status_msg['status_message'].append("Steg PGP passphrase longer than {}: {}".format(
                    config.PGP_PASSPHRASE_LENGTH, len(form_set.pgp_passphrase_steg.data)))
            else:
                chan.pgp_passphrase_steg = form_set.pgp_passphrase_steg.data
                chan.save()
                status_msg['status_title'] = "Success"
                status_msg['status_message'].append("Changed Steg PGP Passphrase.")

        elif form_post.start_download.data:
            can_download, allow_msg = allowed_access(check_can_download=True)
            board_list_admin, allow_msg = allowed_access(
                check_is_board_list_admin=True, check_admin_board=current_chan)
            if not can_download and not board_list_admin:
                return allow_msg

            daemon_com.set_start_download(form_post.message_id.data)
            status_msg['status_title'] = "Success"
            status_msg['status_message'].append(
                "File download initialized in the background. Give it time to download.")

        elif form_post.submit.data:
            invalid_post = False
            if not form_post.validate():
                for field, errors in form_post.errors.items():
                    if field == "csrf_token":
                        invalid_post = True
                    for error in errors:
                        logger.error("Error in the {} field - {}".format(
                            getattr(form_post, field).label.text, error))

            if invalid_post:
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid Token")

            if not form_post.page_id.data:
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid ID")
            elif settings.enable_captcha and not captcha.validate(page_id=form_post.page_id.data):
                status_msg['status_title'] = "Error"
                status_msg['status_message'].append("Invalid Captcha")

            can_post, allow_msg = allowed_access(
                check_can_post=True)
            board_list_admin, allow_msg = allowed_access(
                check_is_board_list_admin=True, check_admin_board=current_chan)
            if not can_post and not board_list_admin:
                return allow_msg

            if form_post.default_from_address.data:
                chan.default_from_address = form_post.from_address.data
            else:
                chan.default_from_address = None
            chan.save()

            status_msg, result, form_populate = post_message(
                form_post, form_steg, status_msg)

        session['form_populate'] = form_populate
        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_board.board",
                                current_chan=current_chan,
                                current_page=current_page))

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

    chan = Chan.query.filter(Chan.address == current_chan).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
    if chan.pgp_passphrase_attach != config.PGP_PASSPHRASE_ATTACH:
        dict_join["pgp_attach"] = chan.pgp_passphrase_attach
    if chan.pgp_passphrase_steg != config.PGP_PASSPHRASE_STEG:
        dict_join["pgp_steg"] = chan.pgp_passphrase_steg
    passphrase_base64_with_pgp = base64.b64encode(
        json.dumps(dict_join).encode()).decode().replace("/", "&")

    return render_template("pages/board.html",
                           board=board,
                           form_populate=form_populate,
                           form_post=form_post,
                           from_list=from_list,
                           get_threads_from_page=get_threads_from_page,
                           page_id=str(uuid.uuid4()),
                           passphrase_base64=passphrase_base64,
                           passphrase_base64_with_pgp=passphrase_base64_with_pgp,
                           status_msg=status_msg,
                           upload_sites=UploadSites)
Exemple #23
0
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)
Exemple #24
0
def mailbox(ident_address, mailbox, page, msg_id):
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    status_msg = {"status_message": []}
    messages = []
    msg_selected = []
    identities = daemon_com.get_identities()
    page = int(page)

    form_mail = forms_mailbox.Mailbox()

    if msg_id != "0":
        if mailbox == "inbox":
            lf = LF()
            if lf.lock_acquire(config.LOCKFILE_API,
                               to=config.API_LOCK_TIMEOUT):
                try:
                    msg_selected = api.getInboxMessageById(msg_id, True)
                    if "inboxMessage" in msg_selected:
                        msg_selected = msg_selected["inboxMessage"][0]
                        expires = get_msg_expires_time(msg_id)
                        if expires:
                            msg_selected["expires_time"] = expires
                except Exception as err:
                    logger.error("Error: {}".format(err))
                finally:
                    time.sleep(config.API_PAUSE)
                    lf.lock_release(config.LOCKFILE_API)

        elif mailbox == "sent":
            lf = LF()
            if lf.lock_acquire(config.LOCKFILE_API,
                               to=config.API_LOCK_TIMEOUT):
                try:
                    msg_selected = api.getSentMessageById(msg_id)
                    if "sentMessage" in msg_selected:
                        msg_selected = msg_selected["sentMessage"][0]
                        expires = get_msg_expires_time(msg_id)
                        if expires:
                            msg_selected["expires_time"] = expires
                except Exception as err:
                    logger.error("Error: {}".format(err))
                finally:
                    time.sleep(config.API_PAUSE)
                    lf.lock_release(config.LOCKFILE_API)

    if request.method == 'POST':
        settings = GlobalSettings.query.first()

        if ((form_mail.messages_per_mailbox_page.data or
             (form_mail.messages_per_mailbox_page.data
              and form_mail.set_per_page.data))
                and form_mail.messages_per_mailbox_page.data !=
                settings.messages_per_mailbox_page):
            settings.messages_per_mailbox_page = form_mail.messages_per_mailbox_page.data
            settings.save()

        elif form_mail.execute_bulk_action.data and form_mail.bulk_action.data:
            msg_ids = request.form.getlist("selected_msg")

            if form_mail.bulk_action.data == "delete":
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API,
                                   to=config.API_LOCK_TIMEOUT):
                    try:
                        for each_id in msg_ids:
                            if mailbox == "inbox":
                                api.trashInboxMessage(each_id)
                            elif mailbox == "sent":
                                api.trashSentMessage(each_id)
                    except Exception as err:
                        logger.error("Error: {}".format(err))
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

                return redirect(
                    url_for("routes_mail.mailbox",
                            ident_address=ident_address,
                            mailbox=mailbox,
                            page="1",
                            msg_id="0"))

            if form_mail.bulk_action.data in ["mark_read", "mark_unread"]:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API,
                                   to=config.API_LOCK_TIMEOUT):
                    try:
                        for each_id in msg_ids:
                            api.getInboxMessageById(
                                each_id,
                                form_mail.bulk_action.data == "mark_read")
                    except Exception as err:
                        logger.error("Error: {}".format(err))
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

                daemon_com.update_unread_mail_count(ident_address)

                return redirect(
                    url_for("routes_mail.mailbox",
                            ident_address=ident_address,
                            mailbox=mailbox,
                            page=page,
                            msg_id=msg_id))

        elif form_mail.reply.data and form_mail.message_id.data:
            lf = LF()
            if lf.lock_acquire(config.LOCKFILE_API,
                               to=config.API_LOCK_TIMEOUT):
                try:
                    msg_selected = api.getInboxMessageById(
                        form_mail.message_id.data, True)
                    if "inboxMessage" in msg_selected:
                        msg_selected = msg_selected["inboxMessage"][0]
                        form_populate = {
                            "to_address":
                            msg_selected["fromAddress"],
                            "body":
                            "\n\n\n------------------------------------------------------\n{}"
                            .format(base64_decode(msg_selected["message"]))
                        }
                        if base64_decode(
                                msg_selected["subject"]).startswith("Re:"):
                            form_populate["subject"] = base64_decode(
                                msg_selected["subject"])
                        else:
                            form_populate["subject"] = "Re: {}".format(
                                base64_decode(msg_selected["subject"]))
                        session['form_populate'] = form_populate
                        session['status_msg'] = status_msg
                except Exception as err:
                    logger.error("Error: {}".format(err))
                finally:
                    time.sleep(config.API_PAUSE)
                    lf.lock_release(config.LOCKFILE_API)

            return redirect(url_for("routes_mail.compose", address_to="0"))

        elif form_mail.forward.data and form_mail.message_id.data:
            lf = LF()
            if lf.lock_acquire(config.LOCKFILE_API,
                               to=config.API_LOCK_TIMEOUT):
                try:
                    msg_selected = api.getInboxMessageById(
                        form_mail.message_id.data, True)
                    if "inboxMessage" in msg_selected:
                        msg_selected = msg_selected["inboxMessage"][0]
                        form_populate = {
                            "body":
                            "\n\n\n------------------------------------------------------\n{}"
                            .format(base64_decode(msg_selected["message"]))
                        }
                        if base64_decode(
                                msg_selected["subject"]).startswith("Fwd:"):
                            form_populate["subject"] = base64_decode(
                                msg_selected["subject"])
                        else:
                            form_populate["subject"] = "Fwd: {}".format(
                                base64_decode(msg_selected["subject"]))
                        session['form_populate'] = form_populate
                        session['status_msg'] = status_msg
                except Exception as err:
                    logger.error("Error: {}".format(err))
                finally:
                    time.sleep(config.API_PAUSE)
                    lf.lock_release(config.LOCKFILE_API)

            return redirect(url_for("routes_mail.compose", address_to="0"))

        elif form_mail.delete.data and form_mail.message_id.data:
            lf = LF()
            if lf.lock_acquire(config.LOCKFILE_API,
                               to=config.API_LOCK_TIMEOUT):
                try:
                    api.trashMessage(form_mail.message_id.data)
                except Exception as err:
                    logger.error("Error: {}".format(err))
                finally:
                    time.sleep(config.API_PAUSE)
                    lf.lock_release(config.LOCKFILE_API)

            return redirect(
                url_for("routes_mail.mailbox",
                        ident_address=ident_address,
                        mailbox=mailbox,
                        page=page,
                        msg_id="0"))

    if ident_address != '0' and mailbox == "inbox":
        daemon_com.update_unread_mail_count(ident_address)

    total_mail_counts = {}
    unread_mail_counts = {}
    for each_identity in Identity.query.all():
        unread_mail_counts[
            each_identity.address] = each_identity.unread_messages
        total_mail_counts[each_identity.address] = each_identity.total_messages

    return render_template("mailbox/mailbox.html",
                           base64_decode=base64_decode,
                           get_messages_from_page=get_messages_from_page,
                           ident_address=ident_address,
                           identities=identities,
                           mailbox=mailbox,
                           msg_id=msg_id,
                           msg_selected=msg_selected,
                           messages=messages,
                           page=page,
                           status_msg=status_msg,
                           timestamp_format=timestamp_format,
                           total_mail_counts=total_mail_counts,
                           unread_mail_counts=unread_mail_counts)
Exemple #25
0
def address_book():
    global_admin, allow_msg = allowed_access(
        check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_addres_book = forms_settings.AddressBook()
    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_addres_book.add.data:
            if not form_addres_book.label.data or not form_addres_book.address.data:
                status_msg['status_message'].append("Label and address required")

            if not status_msg['status_message']:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API, to=config.API_LOCK_TIMEOUT):
                    try:
                        label = base64.b64encode(form_addres_book.label.data.encode()).decode()
                        try:
                            return_str = api.addAddressBookEntry(
                                form_addres_book.address.data, label)
                        except Exception as e:
                            if e:
                                return_str = "Could not add to Address Book: {}".format(e)
                            else:
                                return_str = "Not a valid address?"

                        if return_str:
                            if "Added address" in return_str:
                                new_add_book = AddressBook()
                                new_add_book.address = form_addres_book.address.data
                                new_add_book.label = form_addres_book.label.data
                                new_add_book.save()

                                daemon_com.refresh_address_book()
                                status_msg['status_title'] = "Success"
                                status_msg['status_message'].append(
                                    "Added Address Book entry {}".format(
                                        form_addres_book.label.data))
                                status_msg['status_message'].append(
                                    "Give the system a few seconds for the change to take effect.")
                            else:
                                status_msg['status_message'].append(return_str)
                        else:
                            status_msg['status_message'].append(
                                "Error creating Address Book entry")
                    finally:
                        time.sleep(config.API_PAUSE)
                        lf.lock_release(config.LOCKFILE_API)

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

            if not status_msg['status_message']:
                add_book = AddressBook.query.filter(
                    AddressBook.address == form_addres_book.address.data).first()
                if add_book:
                    add_book.label = form_addres_book.add_label.data
                    add_book.save()
                    daemon_com.refresh_address_book()
                    status_msg['status_title'] = "Success"
                    status_msg['status_message'].append("Address Book entry renamed.")
                    status_msg['status_message'].append(
                        "Give the system a few seconds for the change to take effect.")

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

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

            if not status_msg['status_message']:
                lf = LF()
                if lf.lock_acquire(config.LOCKFILE_API, to=config.API_LOCK_TIMEOUT):
                    try:
                        return_str = api.deleteAddressBookEntry(form_addres_book.address.data)
                        if "Deleted address book entry" in return_str:
                            if add_book:
                                add_book.delete()
                            daemon_com.refresh_address_book()
                            status_msg['status_title'] = "Success"
                            status_msg['status_message'].append("Address Book entry 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 Address Book entry: {}".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_address_book.address_book"))

    return render_template("pages/address_book.html",
                           form_addres_book=form_addres_book,
                           status_msg=status_msg)
Exemple #26
0
def pgp():
    global_admin, allow_msg = allowed_access(check_is_global_admin=True)
    if not global_admin:
        return allow_msg

    form_pgp = forms_settings.PGP()

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

    gnupg_home = "/usr/local/bitchan/gnupg"

    if not os.path.exists(gnupg_home):
        os.mkdir(gnupg_home)

    gpg = gnupg.GPG(gnupghome=gnupg_home)

    private_keys = gpg.list_keys(True)
    public_keys = gpg.list_keys()

    private_key_ids = []
    for each_key in private_keys:
        private_key_ids.append(each_key["keyid"])

    public_key_ids = []
    for each_key in public_keys:
        if each_key["keyid"] not in private_key_ids:
            public_key_ids.append(each_key["keyid"])

    exported_public_keys = {}
    for each_pub_key in public_keys:
        exported_public_keys[each_pub_key["keyid"]] = gpg.export_keys(
            each_pub_key["keyid"])

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

    elif request.method == 'POST':
        if form_pgp.create_master_key.data:
            key_type = form_pgp.key_type_length.data.split(",")[0]
            key_length = int(form_pgp.key_type_length.data.split(",")[1])

            input_data = gpg.gen_key_input(key_type=key_type,
                                           key_length=key_length,
                                           key_usage='encrypt, sign',
                                           name_comment=form_pgp.comment.data,
                                           expire_date=0,
                                           name_real=form_pgp.name.data,
                                           name_email=form_pgp.email.data,
                                           passphrase=form_pgp.passphrase.data)
            key = gpg.gen_key(input_data)

            status_msg['status_message'].append(
                "PGP key pair created: {}".format(key.fingerprint))
            status_msg['status_title'] = "Success"

        elif form_pgp.delete_all.data:
            shutil.rmtree(gnupg_home)
            # for each_key in gpg.list_keys(True):
            #     status_msg['status_message'].append("Delete Private Key {}: {}".format(
            #         each_key["fingerprint"],
            #         gpg.delete_keys(fingerprints=each_key["fingerprint"],
            #                         secret=True,
            #                         passphrase="PASS").status))
            # for each_key in gpg.list_keys():
            #     status_msg['status_message'].append("Delete Public Key {}: {}".format(
            #         each_key["fingerprint"],
            #         gpg.delete_keys(fingerprints=each_key["fingerprint"]).status))
            status_msg['status_message'].append("Deleted all keys")
            status_msg['status_title'] = "Success"

        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_pgp.pgp"))

    return render_template("pages/pgp.html",
                           exported_public_keys=exported_public_keys,
                           private_keys=private_keys,
                           public_key_ids=public_key_ids,
                           public_keys=public_keys,
                           status_msg=status_msg)