Esempio n. 1
0
    def __init__(self, engine, db):
        assert type(engine) is chord.ChordEngine

        self.engine = engine
        self.db = db
        self.loop = engine.loop

        self.latest_version_number = None
        self.latest_version_data = None

        self.auto_publish_enabled = True
        self.auto_scan_enabled = True

        self.csrf_token = base58.encode(os.urandom(64))

        self._dmail_engine = None

        self._running = False

        self._data_key =\
            mbase32.decode("sp1nara3xhndtgswh7fznt414we4mi3y6kdwbkz4jmt8ocb6x"\
                "4w1faqjotjkcrefta11swe3h53dt6oru3r13t667pr7cpe3ocxeuma")
        self._path = b"latest_version"

        self._dmail_autoscan_processes = {}
Esempio n. 2
0
def _count_unread_dmails(dispatcher, addr=None, tag=None):
    if addr and type(addr) not in (bytes, bytearray):
        addr = mbase32.decode(addr)

    def dbcall():
        with dispatcher.node.db.open_session() as sess:
            q = sess.query(func.count("*"))

            q = q.filter(DmailMessage.read == False)

            if addr:
                q = q.filter(DmailMessage.address.has(DmailAddress.site_key == addr))

            if tag == "Trash":
                q = q.filter(DmailMessage.hidden == True).filter(DmailMessage.deleted == False)
                return q.scalar()

            if tag:
                q = q.filter(DmailMessage.tags.any(DmailTag.name == tag))
            q = q.filter(DmailMessage.hidden == False)

            return q.scalar()

    cnt = yield from dispatcher.node.loop.run_in_executor(None, dbcall)

    return cnt
Esempio n. 3
0
def main():
    log.info("Testing...")

    r = generate_targeted_block(\
        mbase32.decode("yyyyyyyy"), 20, b"test data message", 0, 4)

    log.info("Done, r=[{}].".format(r))
Esempio n. 4
0
    def scan_dmail_address(self, addr, significant_bits, key_callback=None):
        if log.isEnabledFor(logging.INFO):
            log.info("Scanning dmail [{}].".format(mbase32.encode(addr)))

        def dbcall():
            with self.db.open_session() as sess:
                q = sess.query(db.DmailAddress)\
                    .filter(db.DmailAddress.site_key == addr)

                dmail_address = q.first()
                if dmail_address:
                    dmail_address.keys
                    sess.expunge_all()

                return dmail_address

        dmail_address = yield from self.loop.run_in_executor(None, dbcall)

        if dmail_address:
            log.info("Found DmailAddress locally, using local settings.")

            target = dmail_address.keys[0].target_key
            significant_bits = dmail_address.keys[0].difficulty
        else:
            log.info("DmailAddress not found locally, fetching settings from"\
                " the network.")

            addr, dsite = yield from\
                self.fetch_recipient_dmail_site(addr, significant_bits)

            if not dsite:
                raise DmailException("Dmail site not found.")

            target = dsite.root["target"]
            significant_bits = dsite.root["difficulty"]

            target = mbase32.decode(target)

        start = target

        while True:
            data_rw = yield from self.task_engine.send_find_key(\
                start, target_key=target, significant_bits=significant_bits,\
                retry_factor=100)

            key = data_rw.data_key

            if not key:
                break

            if log.isEnabledFor(logging.INFO):
                log.info("Found dmail key: [{}].".format(mbase32.encode(key)))

            if key_callback:
                key_callback(key)

            start = key
Esempio n. 5
0
def decode_key(encoded):
    assert consts.NODE_ID_BITS == 512
    assert type(encoded) is str, type(encoded)

    significant_bits = None

    kl = len(encoded)

    if kl == 128:
        data_key = bytes.fromhex(encoded)
    elif kl in (102, 103):
        data_key = bytes(mbase32.decode(encoded))
        if len(data_key) < consts.NODE_ID_BYTES:
            significant_bits = 5 * kl
    else:
        data_key = mbase32.decode(encoded, False)
        significant_bits = 5 * kl

    return data_key, significant_bits
Esempio n. 6
0
def decode_key(encoded):
    assert consts.NODE_ID_BITS == 512
    assert type(encoded) is str, type(encoded)

    significant_bits = None

    kl = len(encoded)

    if kl == 128:
        data_key = bytes.fromhex(encoded)
    elif kl in (102, 103):
        data_key = bytes(mbase32.decode(encoded))
        if len(data_key) < consts.NODE_ID_BYTES:
            significant_bits = 5 * kl
    else:
        data_key = mbase32.decode(encoded, False)
        significant_bits = 5 * kl

    return data_key, significant_bits
Esempio n. 7
0
        def dbcall():
            with self.db.open_session() as sess:
                dmailaddress = db.DmailAddress()
                dmailaddress.site_key = data_key
                dmailaddress.site_privatekey = privkey._encode_key()

                dmailkey = db.DmailKey()
                dmailkey.x = sshtype.encodeMpint(dms.dh.x)
                dmailkey.target_key = mbase32.decode(dms.root["target"])
                dmailkey.difficulty = difficulty

                dmailaddress.keys.append(dmailkey)

                sess.add(dmailaddress)
                sess.commit()
Esempio n. 8
0
        def dbcall():
            with self.db.open_session() as sess:
                dmailaddress = db.DmailAddress()
                dmailaddress.site_key = data_key
                dmailaddress.site_privatekey = privkey._encode_key()
                dmailaddress.scan_interval = 60

                dmailkey = db.DmailKey()
                dmailkey.x = sshtype.encodeMpint(dms.dh.x)
                dmailkey.target_key = mbase32.decode(dms.root["target"])
                dmailkey.difficulty = difficulty

                dmailaddress.keys.append(dmailkey)

                sess.add(dmailaddress)
                sess.commit()
Esempio n. 9
0
    def send_store_targeted_data(\
            self, data, store_key=False, key_callback=None):
        data_enc = base58.encode(data)

        r = yield from\
            self.send_command(\
                "storetargetedblockenc {} {}".format(data_enc, store_key))

        p0 = r.find(b']')
        data_key = mbase32.decode(r[10:p0].decode("UTF-8"))

        key_callback(data_key)

        p0 = r.find(b"storing_nodes=[", p0) + 15
        p1 = r.find(b']', p0)

        return int(r[p0:p1])
Esempio n. 10
0
    def send_store_targeted_data(\
            self, data, store_key=False, key_callback=None):
        data_enc = base58.encode(data)

        r = yield from\
            self.send_command(\
                "storetargetedblockenc {} {}".format(data_enc, store_key))

        p0 = r.find(b']')
        data_key = mbase32.decode(r[10:p0].decode("UTF-8"))

        key_callback(data_key)

        p0 = r.find(b"storing_nodes=[", p0) + 15
        p1 = r.find(b']', p0)

        return int(r[p0:p1])
Esempio n. 11
0
    def do_findkey(self, arg):
        "<DATA_KEY_PREFIX> [TARGET_ID] [SIGNIFICANT_BITS] search the network"
        " for the given key."

        args = arg.split(" ")

        data_key, significant_bits = decode_key(args[0])
        target_key = mbase32.decode(args[1]) if len(args) >= 2 else None
        if len(args) == 3:
            significant_bits = int(args[2])

        start = datetime.today()
        data_rw = yield from self.peer.engine.tasks.send_find_key(
            data_key, significant_bits=significant_bits, target_key=target_key
        )
        diff = datetime.today() - start
        data_key_enc = mbase32.encode(data_rw.data_key) if data_rw.data_key else None
        self.writeln("data_key=[{}].".format(data_key_enc))
        self.writeln("send_find_key(..) took: {}.".format(diff))
Esempio n. 12
0
def _empty_trash(dispatcher, addr_enc):
    addr_site_key = mbase32.decode(addr_enc)

    def dbcall():
        with dispatcher.node.db.open_session() as sess:
            # Immediately delete messages that we sent.
            q = (
                sess.query(DmailMessage)
                .filter(DmailMessage.address.has(DmailAddress.site_key == addr_site_key))
                .filter(DmailMessage.hidden == True)
                .filter(DmailMessage.destination_dmail_key != None)
            )

            q.delete(synchronize_session=False)

            # Mark messages that we received for deletion later. We can't
            # actually delete them until we no longer check the target_id they
            # came from, else we will pick them up again.
            q = (
                sess.query(DmailMessage)
                .filter(DmailMessage.address.has(DmailAddress.site_key == addr_site_key))
                .filter(DmailMessage.hidden == True)
            )

            msgs = q.all()

            for msg in msgs:
                msg.tags.clear()
                msg.sender_dmail_key = None
                msg.destination_dmail_key = None
                msg.destination_significant_bits = None
                msg.subject = ""
                msg.date = mutil.utc_datetime()
                msg.parts.clear()
                msg.read = False
                msg.hidden = True
                msg.deleted = True

            sess.commit()

    yield from dispatcher.node.loop.run_in_executor(None, dbcall)
Esempio n. 13
0
    def do_findkey(self, arg):
        "<DATA_KEY_PREFIX> [TARGET_ID] [SIGNIFICANT_BITS] search the network"
        " for the given key."

        args = arg.split(' ')

        data_key, significant_bits = decode_key(args[0])
        target_key = mbase32.decode(args[1]) if len(args) >= 2 else None
        if len(args) == 3:
            significant_bits = int(args[2])

        start = datetime.today()
        data_rw = yield from\
            self.peer.engine.tasks.send_find_key(\
                data_key, significant_bits=significant_bits,\
                    target_key=target_key)
        diff = datetime.today() - start
        data_key_enc =\
            mbase32.encode(data_rw.data_key) if data_rw.data_key else None
        self.writeln("data_key=[{}].".format(data_key_enc))
        self.writeln("send_find_key(..) took: {}.".format(diff))
Esempio n. 14
0
    def send_store_updateable_key(\
            self, data, privkey, path=None, version=None, store_key=True,\
            key_callback=None):
        privkey_enc = base58.encode(privkey._encode_key())
        data_enc = base58.encode(data)

        cmd = "storeukeyenc {} {} {} {}"\
            .format(privkey_enc, data_enc, version, store_key)

        r = yield from self.send_command(cmd)

        if not r:
            return 0

        if key_callback:
            p1 = r.find(b']', 10)
            r = r[10:p1].decode()
            key_enc = r
            key_callback(mbase32.decode(key_enc))

        return 1  #FIXME: The shell API doesn't return this value as of yet.
Esempio n. 15
0
    def send_store_updateable_key(\
            self, data, privkey, path=None, version=None, store_key=True,\
            key_callback=None):
        privkey_enc = base58.encode(privkey._encode_key())
        data_enc = base58.encode(data)

        cmd = "storeukeyenc {} {} {} {}"\
            .format(privkey_enc, data_enc, version, store_key)

        r = yield from self.send_command(cmd)

        if not r:
            return 0

        if key_callback:
            p1 = r.find(b']', 10)
            r = r[10:p1].decode()
            key_enc = r
            key_callback(mbase32.decode(key_enc))

        return 1 #FIXME: The shell API doesn't return this value as of yet.
Esempio n. 16
0
    def send_find_key(self, prefix, target_key=None, significant_bits=None):
        cmd = "findkey " + mbase32.encode(prefix)
        if target_key:
            cmd += " " + mbase32.encode(target_key)
            if significant_bits:
                cmd += " " + str(significant_bits)

        r = yield from self.send_command(cmd)

        p0 = r.find(b"data_key=[") + 10
        p1 = r.find(b']', p0)

        data_key = r[p0:p1].decode()

        if data_key == "None":
            data_key = None
        else:
            data_key = mbase32.decode(data_key)

        data_rw = chord_tasks.DataResponseWrapper(data_key)

        return data_rw
Esempio n. 17
0
    def send_find_key(self, prefix, target_key=None, significant_bits=None):
        cmd = "findkey " + mbase32.encode(prefix)
        if target_key:
            cmd += " " + mbase32.encode(target_key)
            if significant_bits:
                cmd += " " + str(significant_bits)

        r = yield from self.send_command(cmd)

        p0 = r.find(b"data_key=[") + 10
        p1 = r.find(b']', p0)

        data_key = r[p0:p1].decode()

        if data_key == "None":
            data_key = None
        else:
            data_key = mbase32.decode(data_key)

        data_rw = chord_tasks.DataResponseWrapper(data_key)

        return data_rw
Esempio n. 18
0
def _load_dmails_for_tag(dispatcher, addr, tag):
    if type(addr) not in (bytes, bytearray):
        addr = mbase32.decode(addr)

    def dbcall():
        with dispatcher.node.db.open_session() as sess:
            q = sess.query(DmailMessage).filter(DmailMessage.address.has(DmailAddress.site_key == addr))

            if tag == "Trash":
                q = q.filter(DmailMessage.hidden == True).filter(DmailMessage.deleted == False)
            else:
                q = q.filter(DmailMessage.tags.any(DmailTag.name == tag)).filter(DmailMessage.hidden == False)

            q = q.order_by(DmailMessage.read, DmailMessage.date.desc())

            msgs = q.all()

            sess.expunge_all()

            return msgs

    msgs = yield from dispatcher.node.loop.run_in_executor(None, dbcall)

    return msgs
Esempio n. 19
0
    def __init__(self, engine, db):
        assert type(engine) is chord.ChordEngine

        self.engine = engine
        self.db = db
        self.loop = engine.loop

        self.latest_version_number = None
        self.latest_version_data = None

        self.auto_publish_enabled = True

        self.csrf_token = base58.encode(os.urandom(64))

        self._dmail_engine = None

        self._running = False

        self._data_key =\
            mbase32.decode("sp1nara3xhndtgswh7fznt414we4mi3y6kdwbkz4jmt8ocb6x"\
                "4w1faqjotjkcrefta11swe3h53dt6oru3r13t667pr7cpe3ocxeuma")
        self._path = b"latest_version"

        self._dmail_autoscan_processes = {}
Esempio n. 20
0
def __main():
    global loop

    log.info("mcc running.")

    parser = argparse.ArgumentParser()
    parser.add_argument(\
        "--address",\
        help="The address of the Morphis node to connect to.",\
        default="127.0.0.1:4250")
    parser.add_argument(\
        "--create-dmail",\
        help="Generate and upload a new dmail site.",\
        action="store_true")
    parser.add_argument(\
        "--dburl",\
        help="Specify the database url to use.")
    parser.add_argument(\
        "--fetch-dmail",
        help="Fetch dmail for specified key_id.")
    parser.add_argument(\
        "-i",\
        help="Read file as stdin.")
    parser.add_argument("--nn", type=int,\
        help="Node instance number.")
    parser.add_argument(\
        "--prefix",\
        help="Specify the prefix for various things (currently --create-dmail"\
            ").")
    parser.add_argument(\
        "--scan-dmail",\
        help="Scan the network for available dmails.")
    parser.add_argument(\
        "--send-dmail",\
        help="Send stdin as a dmail with the specified subject. The"\
            " sender and recipients may be specified at the beginning of the"\
            " data as with email headers: 'from: ' and 'to: '.")
    parser.add_argument(\
        "--stat",\
        help="Report node status.",\
        action="store_true")
    parser.add_argument("-l", dest="logconf",\
        help="Specify alternate logging.ini [IF SPECIFIED, THIS MUST BE THE"\
            " FIRST PARAMETER!].")
    parser.add_argument(\
        "--dmail-target",\
        help="Specify the dmail target to validate dmail against.")
    parser.add_argument(\
        "-x",\
        help="Specify the x (Diffie-Hellman private secret) to use.")

    args = parser.parse_args()

    # Load or generate client mcc key.
    key_filename = "data/mcc_key-rsa.mnk"
    if os.path.exists(key_filename):
        log.info("mcc private key file found, loading.")
        client_key = rsakey.RsaKey(filename=key_filename)
    else:
        log.info("mcc private key file missing, generating.")
        client_key = rsakey.RsaKey.generate(bits=4096)
        client_key.write_private_key_file(key_filename)

    # Connect a Morphis Client (lightweight Node) instance.
    mc = client.Client(loop, client_key=client_key, address=args.address)
    r = yield from mc.connect()

    if not r:
        log.warning("Connection failed; exiting.")
        loop.stop()
        return

    dbase = init_db(args)
    de = dmail.DmailEngine(mc, dbase)

    log.info("Processing command requests...")

    if args.stat:
        r = yield from mc.send_command("stat")
        print(r.decode("UTF-8"), end='')

    if args.create_dmail:
        log.info("Creating and uploading dmail site.")

        privkey, data_key, dms =\
            yield from de.generate_dmail_address(args.prefix)

        print("privkey: {}".format(base58.encode(privkey._encode_key())))
        print("x: {}".format(base58.encode(sshtype.encodeMpint(dms.dh.x))))
        print("dmail address: {}".format(mbase32.encode(data_key)))

    if args.send_dmail:
        log.info("Sending dmail.")

        if args.i:
            with open(args.i, "rb") as fh:
                dmail_data = fh.read().decode()
        else:
            dmail_data = stdin.read()

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail_data=[{}].".format(dmail_data))

        yield from de.send_dmail_text(args.send_dmail, dmail_data)

    if args.scan_dmail:
        log.info("Scanning dmail address.")

        addr, sig_bits = mutil.decode_key(args.scan_dmail)

        def key_callback(key):
            print("dmail key: [{}].".format(mbase32.encode(key)))

        yield from de.scan_dmail_address(\
            addr, sig_bits, key_callback=key_callback)

    if args.fetch_dmail:
        log.info("Fetching dmail for key=[{}].".format(args.fetch_dmail))

        key = mbase32.decode(args.fetch_dmail)

        if args.x:
            l, x_int = sshtype.parseMpint(base58.decode(args.x))
        else:
            x_int = None

        dmail_target = args.dmail_target

        dm, valid_sig = yield from de.fetch_dmail(key, x_int, dmail_target)

        if not dm:
            raise Exception("No dmail found.")

        if not x_int:
            print("Encrypted dmail data=[\n{}].".format(mutil.hex_dump(dm)))
        else:
            print("Subject: {}\n".format(dm.subject))

            if dm.sender_pubkey:
                print("From: {}"\
                    .format(mbase32.encode(enc.generate_ID(dm.sender_pubkey))))

            i = 0
            for part in dm.parts:
                print("DmailPart[{}]:\n    mime-type=[{}]\n    data=[{}]\n"\
                    .format(i, part.mime_type, part.data))
                i += 1

    log.info("Disconnecting.")

    yield from mc.disconnect()

    loop.stop()
Esempio n. 21
0
    def __send_dmail(self, from_asymkey, recipient, dmail):
        assert type(recipient) is DmailSite

        root = recipient.root
        sse = sshtype.parseMpint(base58.decode(root["sse"]))[1]
        target = root["target"]
        difficulty = root["difficulty"]

        dh = dhgroup14.DhGroup14()
        dh.generate_x()
        dh.generate_e()
        dh.f = sse

        k = dh.calculate_k()

        target_key = mbase32.decode(target)

        key = self._generate_encryption_key(target_key, k)
        dmail_bytes = dmail.encode()

        m, r = enc.encrypt_data_block(dmail_bytes, key)
        if m:
            if r:
                m = m + r
        else:
            m = r

        dw = DmailWrapper()
        dw.ssm = _dh_method_name
        dw.sse = sse
        dw.ssf = dh.e

        if from_asymkey:
            dw.signature = from_asymkey.calc_rsassa_pss_sig(m)
        else:
            dw.signature = b''

        dw.data_len = len(dmail_bytes)
        dw.data_enc = m

        tb = mp.TargetedBlock()
        tb.target_key = target_key
        tb.noonce = int(0).to_bytes(64, "big")
        tb.block = dw

        tb_data = tb.encode()
        tb_header = tb_data[:mp.TargetedBlock.BLOCK_OFFSET]

        if log.isEnabledFor(logging.INFO):
            log.info(\
                "Attempting work on dmail (target=[{}], difficulty=[{}])."\
                    .format(target, difficulty))

        def threadcall():
            return brute.generate_targeted_block(\
                target_key, difficulty, tb_header,\
                mp.TargetedBlock.NOONCE_OFFSET,\
                mp.TargetedBlock.NOONCE_SIZE)

        noonce_bytes = yield from self.loop.run_in_executor(None, threadcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Work found noonce [{}].".format(noonce_bytes))

        mp.TargetedBlock.set_noonce(tb_data, noonce_bytes)

        if log.isEnabledFor(logging.INFO):
            mp.TargetedBlock.set_noonce(tb_header, noonce_bytes)
            log.info("hash=[{}]."\
                .format(mbase32.encode(enc.generate_ID(tb_header))))

        key = None

        def key_callback(val):
            nonlocal key
            key = val

        log.info("Sending dmail to the network.")

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail block data=[\n{}]."\
                .format(mutil.hex_dump(tb_data)))

        total_storing = 0
        retry = 0
        while True:
            storing_nodes = yield from\
                self.task_engine.send_store_targeted_data(\
                    tb_data, store_key=True, key_callback=key_callback,\
                    retry_factor=retry * 10)

            total_storing += storing_nodes

            if total_storing >= 3:
                break

            if retry > 32:
                break
            elif retry > 3:
                yield from asyncio.sleep(1)

            retry += 1

        key_enc = mbase32.encode(key)
        id_enc = mbase32.encode(enc.generate_ID(key))

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail sent; key=[{}], id=[{}], storing_nodes=[{}]."\
                .format(key_enc, id_enc, total_storing))

        return total_storing
Esempio n. 22
0
    def scan_dmail_address(self, addr, significant_bits, key_callback=None):
        addr_enc = mbase32.encode(addr)

        if log.isEnabledFor(logging.INFO):
            log.info("Scanning dmail [{}].".format(addr_enc))

        def dbcall():
            with self.db.open_session() as sess:
                q = sess.query(db.DmailAddress)\
                    .filter(db.DmailAddress.site_key == addr)

                dmail_address = q.first()
                if dmail_address:
                    dmail_address.keys
                    sess.expunge_all()

                return dmail_address

        dmail_address = yield from self.loop.run_in_executor(None, dbcall)

        if dmail_address:
            log.info("Found DmailAddress locally, using local settings.")

            target = dmail_address.keys[0].target_key
            significant_bits = dmail_address.keys[0].difficulty
        else:
            log.info("DmailAddress not found locally, fetching settings from"\
                " the network.")

            dsites = yield from\
                self.fetch_recipient_dmail_sites(\
                    [(addr_enc, addr, significant_bits)])

            if not dsites:
                raise DmailException("Dmail site not found.")

            dsite = dsites[0]

            target = dsite.root["target"]
            significant_bits = dsite.root["difficulty"]

            target = mbase32.decode(target)

        start = target

        while True:
            data_rw = yield from self.task_engine.send_find_key(\
                start, target_key=target, significant_bits=significant_bits,\
                retry_factor=100)

            key = data_rw.data_key

            if not key:
                break

            if log.isEnabledFor(logging.INFO):
                log.info("Found dmail key: [{}].".format(mbase32.encode(key)))

            if key_callback:
                key_callback(key)

            start = key
Esempio n. 23
0
    def _send_dmail(self, from_asymkey, recipient, dmail_bytes, signature):
        assert type(recipient) is DmailSite

        # Read in recipient DmailSite.
        root = recipient.root
        sse = sshtype.parseMpint(base58.decode(root["sse"]))[1]
        target_enc = root["target"]
        difficulty = root["difficulty"]

        # Calculate a shared secret.
        dh = dhgroup14.DhGroup14()
        dh.generate_x()
        dh.generate_e()
        dh.f = sse

        k = dh.calculate_k()

        target_key = mbase32.decode(target_enc)

        key = self._generate_encryption_key(target_key, k)

        # Encrypt the Dmail bytes.
        m, r = enc.encrypt_data_block(dmail_bytes, key)
        if m:
            if r:
                m = m + r
        else:
            m = r

        # Store it in a DmailWrapper.
        dw = DmailWrapper()
        dw.ssm = _dh_method_name
        dw.sse = sse
        dw.ssf = dh.e

        dw.data_len = len(dmail_bytes)
        dw.data_enc = m

        # Store the DmailWrapper in a TargetedBlock.
        tb = mp.TargetedBlock()
        tb.target_key = target_key
        tb.nonce = int(0).to_bytes(64, "big")
        tb.block = dw

        tb_data = tb.encode()
        tb_header = tb_data[:mp.TargetedBlock.BLOCK_OFFSET]

        # Do the POW on the TargetedBlock.
        if log.isEnabledFor(logging.INFO):
            log.info(\
                "Attempting work on dmail (target=[{}], difficulty=[{}])."\
                    .format(target_enc, difficulty))

        def threadcall():
            return brute.generate_targeted_block(\
                target_key, difficulty, tb_header,\
                mp.TargetedBlock.NOONCE_OFFSET,\
                mp.TargetedBlock.NOONCE_SIZE)

        nonce_bytes = yield from self.loop.run_in_executor(None, threadcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Work found nonce [{}].".format(nonce_bytes))

        mp.TargetedBlock.set_nonce(tb_data, nonce_bytes)

        if log.isEnabledFor(logging.INFO):
            mp.TargetedBlock.set_nonce(tb_header, nonce_bytes)
            log.info("Message key=[{}]."\
                .format(mbase32.encode(enc.generate_ID(tb_header))))

        key = None

        def key_callback(val):
            nonlocal key
            key = val

        if log.isEnabledFor(logging.DEBUG):
            log.debug("TargetedBlock dump=[\n{}]."\
                .format(mutil.hex_dump(tb_data)))

        # Upload the TargetedBlock to the network.
        log.info("Sending dmail to the network.")

        total_storing = 0
        retry = 0
        while True:
            storing_nodes = yield from\
                self.task_engine.send_store_targeted_data(\
                    tb_data, store_key=True, key_callback=key_callback,\
                    retry_factor=retry * 10)

            total_storing += storing_nodes

            if total_storing >= 3:
                break

            if retry > 32:
                break
            elif retry > 3:
                yield from asyncio.sleep(1)

            retry += 1

        key_enc = mbase32.encode(key)
        id_enc = mbase32.encode(enc.generate_ID(key))

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail sent; key=[{}], id=[{}], storing_nodes=[{}]."\
                .format(key_enc, id_enc, total_storing))

        return total_storing
Esempio n. 24
0
def _list_dmails_for_tag(dispatcher, addr, tag):
    msgs = yield from _load_dmails_for_tag(dispatcher, addr, tag)

    if type(addr) is str:
        addr_enc = addr
    else:
        addr_enc = mbase32.decode(addr)

    if not msgs:
        dispatcher.send_partial_content('<tr><td colspan="6">No messages.</td><tr></table>')
        return

    row_template = templates.dmail_msg_list_list_row[0]

    show_sender = tag not in ("Outbox", "Sent", "Drafts")

    for msg in msgs:
        unread = "" if msg.read else "new-mail"

        mail_icon = "new-mail-icon" if unread else "mail-icon"

        subject = msg.subject
        if subject:
            subject = subject.replace('"', "&quot;")
        else:
            subject = "[no subject]"

        safe_reply_subject = generate_safe_reply_subject(msg)

        if show_sender:
            addr_key = msg.sender_dmail_key
            if addr_key:
                addr_key_enc = mbase32.encode(addr_key)
                if msg.sender_valid:
                    addr_value = addr_key_enc
                else:
                    addr_value = '<span class="strikethrough">' + addr_key_enc + "</span>"
            else:
                addr_value = None
        else:
            addr_key = msg.destination_dmail_key
            if addr_key:
                addr_value = mbase32.encode(addr_key)
            else:
                addr_value = None

        if not addr_value:
            addr_value = "[Anonymous]"

        row = row_template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            mail_icon=mail_icon,
            tag=tag,
            unread=unread,
            addr=addr_enc,
            msg_id=msg.id,
            subject=subject,
            safe_reply_subject=safe_reply_subject,
            sender=addr_value,
            timestamp=mutil.format_human_no_ms_datetime(msg.date),
        )

        dispatcher.send_partial_content(row)
Esempio n. 25
0
def __main():
    global loop

    log.info("mcc running.")

    parser = argparse.ArgumentParser()
    parser.add_argument(\
        "--address",\
        help="The address of the Morphis node to connect to.",\
        default="127.0.0.1:4250")
    parser.add_argument(\
        "--create-dmail",\
        help="Generate and upload a new dmail site.",\
        action="store_true")
    parser.add_argument(\
        "--dburl",\
        help="Specify the database url to use.")
    parser.add_argument(\
        "--fetch-dmail",
        help="Fetch dmail for specified key_id.")
    parser.add_argument(\
        "-i",\
        help="Read file as stdin.")
    parser.add_argument("--nn", type=int,\
        help="Node instance number.")
    parser.add_argument(\
        "--prefix",\
        help="Specify the prefix for various things (currently --create-dmail"\
            ").")
    parser.add_argument(\
        "--scan-dmail",\
        help="Scan the network for available dmails.")
    parser.add_argument(\
        "--send-dmail",\
        help="Send stdin as a dmail with the specified subject. The"\
            " sender and recipients may be specified at the beginning of the"\
            " data as with email headers: 'from: ' and 'to: '.")
    parser.add_argument(\
        "--stat",\
        help="Report node status.",\
        action="store_true")
    parser.add_argument("-l", dest="logconf",\
        help="Specify alternate logging.ini [IF SPECIFIED, THIS MUST BE THE"\
            " FIRST PARAMETER!].")
    parser.add_argument(\
        "--dmail-target",\
        help="Specify the dmail target to validate dmail against.")
    parser.add_argument(\
        "-x",\
        help="Specify the x (Diffie-Hellman private secret) to use.")

    args = parser.parse_args()

    # Load or generate client mcc key.
    key_filename = "data/mcc_key-rsa.mnk"
    if os.path.exists(key_filename):
        log.info("mcc private key file found, loading.")
        client_key = rsakey.RsaKey(filename=key_filename)
    else:
        log.info("mcc private key file missing, generating.")
        client_key = rsakey.RsaKey.generate(bits=4096)
        client_key.write_private_key_file(key_filename)

    # Connect a Morphis Client (lightweight Node) instance.
    mc = client.Client(loop, client_key=client_key, address=args.address)
    r = yield from mc.connect()

    if not r:
        log.warning("Connection failed; exiting.")
        loop.stop()
        return

    dbase = init_db(args)
    de = dmail.DmailEngine(mc, dbase)

    log.info("Processing command requests...")

    if args.stat:
        r = yield from mc.send_command("stat")
        print(r.decode("UTF-8"), end='')

    if args.create_dmail:
        log.info("Creating and uploading dmail site.")

        privkey, data_key, dms, storing_nodes =\
            yield from de.generate_dmail_address(args.prefix)

        print("privkey: {}".format(base58.encode(privkey._encode_key())))
        print("x: {}".format(base58.encode(sshtype.encodeMpint(dms.dh.x))))
        print("dmail address: {}".format(mbase32.encode(data_key)))
        print("storing_nodes=[{}]."\
            .format(base58.encode(privkey._encode_key())))

    if args.send_dmail:
        log.info("Sending dmail.")

        if args.i:
            with open(args.i, "rb") as fh:
                dmail_data = fh.read().decode()
        else:
            dmail_data = stdin.read()

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail_data=[{}].".format(dmail_data))

        yield from de.send_dmail_text(args.send_dmail, dmail_data)

    if args.scan_dmail:
        log.info("Scanning dmail address.")

        addr, sig_bits = mutil.decode_key(args.scan_dmail)

        def key_callback(key):
            print("dmail key: [{}].".format(mbase32.encode(key)))

        yield from de.scan_dmail_address(\
            addr, sig_bits, key_callback=key_callback)

    if args.fetch_dmail:
        log.info("Fetching dmail for key=[{}].".format(args.fetch_dmail))

        key = mbase32.decode(args.fetch_dmail)

        if args.x:
            l, x_int = sshtype.parseMpint(base58.decode(args.x))
        else:
            x_int = None

        dmail_target = args.dmail_target

        dm, valid_sig = yield from de.fetch_dmail(key, x_int, dmail_target)

        if not dm:
            raise Exception("No dmail found.")

        if not x_int:
            print("Encrypted dmail data=[\n{}].".format(mutil.hex_dump(dm)))
        else:
            print("Subject: {}\n".format(dm.subject))

            if dm.sender_pubkey:
                print("From: {}"\
                    .format(mbase32.encode(enc.generate_ID(dm.sender_pubkey))))

            i = 0
            for part in dm.parts:
                print("DmailPart[{}]:\n    mime-type=[{}]\n    data=[{}]\n"\
                    .format(i, part.mime_type, part.data))
                i += 1

    log.info("Disconnecting.")

    yield from mc.disconnect()

    loop.stop()
Esempio n. 26
0
def serve_get(dispatcher, rpath):
    global top_tags

    log.info("Service .dmail request.")

    req = rpath[len(s_dmail) :]

    #    if log.isEnabledFor(logging.INFO):
    #        log.info("req=[{}].".format(req))

    if req == "" or req == "/" or req == "/goto_new_mail" or req.startswith("/wrapper/"):
        cacheable = False
        if req == "/goto_new_mail":
            tag = "Inbox"
            addr = yield from _load_first_address_with_new_mail(dispatcher)
            if addr:
                log.info("NEW")
                addr_enc = mbase32.encode(addr.site_key)
            else:
                log.info("NO NEW")
                addr_enc = ""
            qline = None
        elif req.startswith("/wrapper/"):
            params = req[9:]

            pq = params.find("?")

            if pq != -1:
                qline = params[pq + 1 :]
                params = params[:pq]
            else:
                qline = None

            p0 = params.find("/")
            if p0 == -1:
                p0 = len(params)
                tag = "Inbox"
            else:
                tag = params[p0 + 1 :]

            addr_enc = params[:p0]

            if addr_enc:
                cacheable = True
                if dispatcher.handle_cache(req):
                    return
        else:
            tag = "Inbox"
            addr_enc = ""
            qline = None

        if not addr_enc:
            dmail_address = yield from _load_default_dmail_address(dispatcher)
            if dmail_address:
                addr_enc = mbase32.encode(dmail_address.site_key)

        msg_list = None
        if qline:
            eparams = parse_qs(qline)
            msg_list = eparams.get("msg_list")
            if msg_list:
                msg_list = msg_list[0]

        if not msg_list:
            msg_list = "morphis://.dmail/msg_list/" + addr_enc + "/" + tag

        template = templates.dmail_page_wrapper[0]
        template = template.format(tag=tag, addr=addr_enc, msg_list_iframe_url=msg_list)

        if cacheable:
            dispatcher.send_content([template, req])
        else:
            dispatcher.send_content(template)
        return

    if req == "/style.css":
        dispatcher.send_content(templates.dmail_css, content_type="text/css")
    elif req == "/logo":
        template = templates.dmail_logo[0]

        current_version = dispatcher.node.morphis_version
        latest_version_number = dispatcher.latest_version_number

        if latest_version_number and current_version != latest_version_number:
            version_str = '<span class="strikethrough nomargin">{}</span>]' '&nbsp;[<a href="{}{}">GET {}</a>'.format(
                current_version,
                dispatcher.handler.maalstroom_url_prefix_str,
                "sp1nara3xhndtgswh7fznt414we4mi3y6kdwbkz4jmt8ocb6"
                "x4w1faqjotjkcrefta11swe3h53dt6oru3r13t667pr7"
                "cpe3ocxeuma/latest_version",
                latest_version_number,
            )
        else:
            version_str = current_version

        connections = dispatcher.connection_count
        if connections == 1:
            connection_str = "1 Connection"
        else:
            connection_str = str(connections) + " Connections"

        template = template.format(version=version_str, connections=connection_str)

        dispatcher.send_content(template)
    elif req.startswith("/nav/"):
        params = req[5:]

        p0 = params.index("/")

        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        template = templates.dmail_nav[0]

        template = template.format(csrf_token=dispatcher.client_engine.csrf_token, addr=addr_enc, tag=tag)

        dispatcher.send_content(template)
    elif req.startswith("/aside/"):
        params = req[7:]
        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        addr = mbase32.decode(addr_enc)

        template = templates.dmail_aside[0]

        fmt = {}

        for top_tag in top_tags:
            active = top_tag == tag
            unread_count = yield from _count_unread_dmails(dispatcher, addr, top_tag)

            fmt[top_tag + "_active"] = "active-mailbox" if active else ""
            fmt[top_tag + "_unread_count"] = unread_count if unread_count else ""
            fmt[top_tag + "_unread_class"] = ("active-notify" if active else "inactive-notify") if unread_count else ""

        tags = yield from _load_tags(dispatcher, top_tags)

        tag_rows = []

        unquoted_tag = unquote(tag)

        for tag in tags:
            if unquoted_tag == tag.name:
                active = " active_tag"
            else:
                active = ""

            row = (
                '<li class="bullet{active}"><span class="mailbox-pad">'
                '<a href="morphis://.dmail/wrapper/{addr}/{tag}">{tag}</a>'
                "</span></li>".format(active=active, addr=addr_enc, tag=tag.name)
            )
            tag_rows.append(row)

        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token, addr=addr_enc, tag=tag, tag_rows="".join(tag_rows), **fmt
        )

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(template)
    elif req.startswith("/msg_list/list/"):
        params = req[15:]
        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = unquote(params[p0 + 1 :])

        template = templates.dmail_msg_list_list_start[0]

        addr_heading = "TO" if tag in ("Outbox", "Sent", "Drafts") else "FROM"

        if tag == "Inbox" or tag == "":
            unread_check = '<meta target="self" http-equiv="refresh" content="60"/>'
        else:
            unread_check = ""

        template = template.format(unread_check=unread_check, addr_heading=addr_heading)

        acharset = dispatcher.get_accept_charset()
        dispatcher.send_partial_content(template, True, content_type="text/html; charset={}".format(acharset))

        yield from _list_dmails_for_tag(dispatcher, addr_enc, unquote(tag))

        dispatcher.send_partial_content(templates.dmail_msg_list_list_end[0])
        dispatcher.end_partial_content()
    elif req.startswith("/msg_list/"):
        params = req[10:]

        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        if dispatcher.handle_cache(req):
            return

        if tag == "Trash":
            empty_trash_button_class = "link-button"
        else:
            empty_trash_button_class = "display_none"

        template = templates.dmail_msg_list[0]
        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            tag=unquote(tag),
            addr=addr_enc,
            empty_trash_button_class=empty_trash_button_class,
        )

        dispatcher.send_content([template, req])
    elif req == "/new_mail":
        template = templates.dmail_new_mail[0]

        unread_count = yield from _count_unread_dmails(dispatcher)

        template = template.format(unread_count=unread_count)

        dispatcher.send_content(template)

    elif req.startswith("/images/"):
        dispatcher.send_content(templates.imgs[req[8:]])

    elif req.startswith("/tag/view/list/"):
        params = req[15:]

        p0 = params.index("/")
        tag = params[:p0]
        addr_enc = params[p0 + 1 :]

        if log.isEnabledFor(logging.INFO):
            log.info("Viewing dmails with tag [{}] for address [{}].".format(tag, addr_enc))

        start = templates.dmail_tag_view_list_start.replace(b"${TAG_NAME}", tag.encode())
        # FIXME: This is getting inefficient now, maybe time for Flask or
        # something like it. Maybe we can use just it's template renderer.
        start = start.replace(b"${DMAIL_ADDRESS}", addr_enc.encode())
        start = start.replace(b"${DMAIL_ADDRESS2}", "{}...".format(addr_enc[:32]).encode())

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_partial_content(start, True, content_type="text/html; charset={}".format(acharset))

        yield from _list_dmails_for_tag(dispatcher, mbase32.decode(addr_enc), tag)

        dispatcher.send_partial_content(templates.dmail_tag_view_list_end)
        dispatcher.end_partial_content()

    elif req.startswith("/read/content/"):
        params = req[14:]

        msg_dbid = params

        dm = yield from _load_dmail(dispatcher, msg_dbid, fetch_parts=True)

        dmail_text = _format_dmail_content(dm)

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(dmail_text.encode(acharset), content_type="text/plain; charset={}".format(acharset))

    elif req.startswith("/read/subject/"):
        params = req[14:]

        msg_dbid = params

        dm = yield from _load_dmail(dispatcher, msg_dbid)

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(dm.subject.encode(acharset), content_type="text/plain; charset={}".format(acharset))

    elif req.startswith("/read/"):
        params = req[6:]

        p0 = params.index("/")
        p1 = params.index("/", p0 + 1)

        addr_enc = params[:p0]
        tag = params[p0 + 1 : p1]
        msg_dbid = params[p1 + 1 :]

        def processor(sess, dm):
            dm.read = True
            return True

        dm = yield from _process_dmail_message(dispatcher, msg_dbid, processor, fetch_parts=True, fetch_tags=True)

        if dm.hidden:
            trash_msg = "REMOVE FROM TRASH"
        else:
            trash_msg = "MOVE TO TRASH"

        safe_reply_subject = generate_safe_reply_subject(dm)

        if dm.sender_dmail_key:
            sender_addr = mbase32.encode(dm.sender_dmail_key)
            if dm.sender_valid:
                sender_class = "valid_sender"
            else:
                sender_class = "invalid_sender"
        else:
            sender_addr = "[Anonymous]"
            sender_class = "valid_sender"

        if dm.destination_dmail_key:
            dest_addr_enc = mbase32.encode(dm.destination_dmail_key)
            dest_class = ""
        else:
            dest_addr_enc = ""
            dest_class = " display_none"

        unquoted_tag = unquote(tag)

        existing_tag_rows = []
        if len(dm.tags) > 1:
            remove_tag_class = ""

            for etag in dm.tags:
                if etag.name == unquoted_tag:
                    selected = "selected "
                else:
                    selected = ""

                row = '<option {selected}value"{tag_id}">{tag_name}</option>'.format(
                    selected=selected, tag_id=etag.id, tag_name=etag.name
                )
                existing_tag_rows.append(row)
        else:
            remove_tag_class = "display_none"

        current_tag_names = [x.name for x in dm.tags]
        current_tag_names.extend(top_tags)
        current_tag_names.remove("Inbox")
        tags = yield from _load_tags(dispatcher, current_tag_names)

        available_tag_rows = []

        for atag in tags:
            row = '<option value"{tag_id}">{tag_name}</option>'.format(tag_id=atag.id, tag_name=atag.name)
            available_tag_rows.append(row)

        template = templates.dmail_read[0]
        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            addr=addr_enc,
            tag=tag,
            safe_reply_subject=safe_reply_subject,
            trash_msg=trash_msg,
            msg_id=msg_dbid,
            sender_class=sender_class,
            sender=sender_addr,
            dest_class=dest_class,
            dest_addr=dest_addr_enc,
            date=mutil.format_human_no_ms_datetime(dm.date),
            remove_tag_class=remove_tag_class,
            existing_tags="".join(existing_tag_rows),
            available_tags="".join(available_tag_rows),
        )

        dispatcher.send_content(template)
    elif req.startswith("/compose/"):
        if len(req) > 8 and req[8] == "/":
            params = req[9:]
        else:
            params = req[8:]

        p0 = params.find("?")
        if p0 != -1:
            eparams = parse_qs(params[p0 + 1 :])

            subject = eparams.get("subject")
            if subject:
                subject = subject[0].replace('"', "&quot;")
            else:
                subject = ""

            sender_addr_enc = eparams.get("sender")
            if sender_addr_enc:
                sender_addr_enc = sender_addr_enc[0]
            else:
                sender_addr_enc = ""

            message_text = eparams.get("message")
            if message_text:
                message_text = message_text[0]
            else:
                message_text = ""
        else:
            subject = ""
            sender_addr_enc = ""
            message_text = ""
            p0 = len(params)

        dest_addr_enc = params[:p0]

        autofocus_fields = {"dest_addr_autofocus": "", "subject_autofocus": "", "message_text_autofocus": ""}
        if not dest_addr_enc:
            autofocus_fields["dest_addr_autofocus"] = " autofocus"
        elif not subject:
            autofocus_fields["subject_autofocus"] = " autofocus"
        elif not message_text:
            autofocus_fields["message_text_autofocus"] = " autofocus"

        addrs = yield from _list_dmail_addresses(dispatcher)

        if sender_addr_enc:
            sender_addr = mbase32.decode(sender_addr_enc)
            default_id = None
        else:
            sender_addr = None
            default_id = yield from _load_default_dmail_address_id(dispatcher)
            if not default_id:
                owner_if_anon_id = ""

        from_addr_options = []

        for addr in addrs:
            if sender_addr:
                selected = addr.site_key.startswith(sender_addr)
            elif default_id:
                selected = addr.id == default_id
            else:
                selected = False

            if selected:
                option = '<option value="{}" selected>{}</option>'
                owner_if_anon_id = addr.id
            else:
                option = '<option value="{}">{}</option>'

            addr_enc = mbase32.encode(addr.site_key)

            from_addr_options.append(option.format(addr.id, addr_enc))

        from_addr_options.append("<option value=" ">[Anonymous]</option>")

        from_addr_options = "".join(from_addr_options)

        template = templates.dmail_compose[0]

        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            delete_class="display_none",
            owner_if_anon=owner_if_anon_id,
            from_addr_options=from_addr_options,
            dest_addr=dest_addr_enc,
            subject=subject,
            message_text=message_text,
            **autofocus_fields
        )

        acharset = dispatcher.get_accept_charset()
        dispatcher.send_content(template, content_type="text/html; charset={}".format(acharset))
    elif req == "/address_list":
        addrs = yield from _list_dmail_addresses(dispatcher)
        default_id = yield from _load_default_dmail_address_id(dispatcher)

        csrf_token = dispatcher.client_engine.csrf_token

        row_template = templates.dmail_address_list_row[0]

        rows = []

        for addr in addrs:
            site_key_enc = mbase32.encode(addr.site_key)

            if default_id and addr.id == default_id:
                set_default_class = "hidden"
            else:
                set_default_class = ""

            if addr.scan_interval:
                autoscan_link_text = "disable autoscan"
                autoscan_interval = 0
            else:
                autoscan_link_text = "enable autoscan"
                autoscan_interval = 60

            resp = row_template.format(
                csrf_token=csrf_token,
                addr=site_key_enc,
                addr_dbid=addr.id,
                set_default_class=set_default_class,
                autoscan_link_text=autoscan_link_text,
                autoscan_interval=autoscan_interval,
            )

            rows.append(resp)

        rows_content = "".join(rows)

        template = templates.dmail_address_list[0]
        template = template.format(address_list=rows_content)

        dispatcher.send_content(template)

    # Actions.

    elif req.startswith("/create_tag?"):
        query = req[12:]

        qdict = parse_qs(query, keep_blank_values=True)

        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        tag_name = qdict["tag_name"][0]

        if not tag_name:
            dispatcher.send_204()
            return

        r = yield from _create_tag(dispatcher, tag_name)

        redirect = qdict.get("redirect")
        if r and redirect:
            dispatcher.send_301(redirect[0])
        else:
            dispatcher.send_204()
    elif req.startswith("/modify_message_tag?"):
        query = req[20:]

        qdict = parse_qs(query, keep_blank_values=True)

        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        submit = qdict["submit"][0]

        def processor(sess, dm):
            if submit == "add_tag":
                dmail.attach_dmail_tag(sess, dm, qdict["add_tag"][0])
                return True
            elif submit == "move_to_tag":
                dm.tags.clear()
                dmail.attach_dmail_tag(sess, dm, qdict["add_tag"][0])
                return True
            elif submit == "remove_tag":
                if len(dm.tags) <= 1:
                    return False

                remove_tag = qdict["remove_tag"][0]
                remove_target = None
                for tag in dm.tags:
                    if tag.name == remove_tag:
                        remove_target = tag
                        break
                dm.tags.remove(remove_target)
                return True
            else:
                return False

        msg_id = qdict["msg_id"][0]

        dm = yield from _process_dmail_message(dispatcher, msg_id, processor, fetch_tags=True)

        redirect = qdict.get("redirect")
        if redirect:
            dispatcher.send_301(redirect[0])
        else:
            dispatcher.send_204()
    elif req.startswith("/refresh/"):
        params = req[9:]

        p0 = params.index("/")

        csrf_token = params[:p0]
        addr_enc = params[p0 + 1 :]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        dmail_address = yield from _load_dmail_address(dispatcher, site_key=mbase32.decode(addr_enc), fetch_keys=True)

        dispatcher.client_engine.trigger_dmail_scan(dmail_address)

        dispatcher.send_204()
    elif req.startswith("/toggle_read/"):
        params = req[13:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        msg_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dm):
            dm.read = not dm.read
            return True

        yield from _process_dmail_message(dispatcher, msg_dbid, processor)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/toggle_trashed/"):
        params = req[16:]
        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        msg_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dm):
            dm.hidden = not dm.hidden
            return True

        yield from _process_dmail_message(dispatcher, msg_dbid, processor)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/set_autoscan/"):
        params = req[14:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)
        p1 = params.index("/", p0 + 1, pq)

        csrf_token = params[:p0]
        addr_id = int(params[p0 + 1 : p1])
        interval = int(params[p1 + 1 : pq])

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, addr):
            addr.scan_interval = interval
            return True

        addr = yield from _process_dmail_address(dispatcher, processor, addr_id, fetch_keys=True)

        dispatcher.client_engine.update_dmail_autoscan(addr)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/empty_trash/"):
        params = req[13:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        addr_enc = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        yield from _empty_trash(dispatcher, addr_enc)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/make_address_default/"):
        params = req[22:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/")

        csrf_token = params[:p0]
        addr_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        yield from _set_default_dmail_address(dispatcher, addr_dbid)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()

    ##### OLD:

    elif req == "/create_address":
        if dispatcher.handle_cache(req):
            return

        template = templates.dmail_create_address[0]

        template = template.format(csrf_token=dispatcher.client_engine.csrf_token)

        dispatcher.send_content([template, req])
    elif req.startswith("/address_config/"):
        params = req[16:]

        addr_enc = params

        dmail_address = yield from _load_dmail_address(dispatcher, site_key=mbase32.decode(addr_enc), fetch_keys=True)

        content = templates.dmail_address_config[0]

        content = content.replace("{csrf_token}", dispatcher.client_engine.csrf_token)
        content = content.replace("${DIFFICULTY}", str(dmail_address.keys[0].difficulty))
        content = content.replace("${DMAIL_ADDRESS_SHORT}", addr_enc[:32])
        content = content.replace("${DMAIL_ADDRESS}", addr_enc)
        content = content.replace("${PRIVATE_KEY}", base58.encode(dmail_address.site_privatekey))
        content = content.replace("${X}", base58.encode(dmail_address.keys[0].x))
        content = content.replace("${TARGET_KEY}", base58.encode(dmail_address.keys[0].target_key))

        dispatcher.send_content([content, None])
    ##### OLD ACTIONS:
    elif req.startswith("/create_address/make_it_so?"):
        query = req[27:]

        qdict = parse_qs(query, keep_blank_values=True)

        prefix = qdict["prefix"][0]
        difficulty = int(qdict["difficulty"][0])
        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        log.info("prefix=[{}].".format(prefix))
        privkey, dmail_key, dms, storing_nodes = yield from _create_dmail_address(dispatcher, prefix, difficulty)

        dmail_key_enc = mbase32.encode(dmail_key)

        dispatcher.send_partial_content(templates.dmail_frame_start, True)
        if storing_nodes:
            dispatcher.send_partial_content(b"SUCCESS<br/>")
        else:
            dispatcher.send_partial_content(
                "PARTIAL SUCCESS<br/>"
                "<p>Your Dmail site was generated successfully; however,"
                " it failed to be stored on the network. To remedy this,"
                " simply go to your Dmail address page and click the"
                ' [<a target="_self" href="morphis://.dmail/'
                'address_config/{}">Address Settings</a>] link, and then'
                ' click the "Republish Dmail Site" button.</p>'.format(dmail_key_enc).encode()
            )

        dispatcher.send_partial_content(
            '<p>New dmail address: <a href="morphis://.dmail/wrapper/'
            '{addr_enc}">{addr_enc}</a></p>'.format(addr_enc=dmail_key_enc).encode()
        )
        dispatcher.send_partial_content(templates.dmail_frame_end)
        dispatcher.end_partial_content()
    elif req.startswith("/save_address_config/publish?"):
        query = req[29:]

        qdict = parse_qs(query, keep_blank_values=True)

        addr_enc = qdict["dmail_address"][0]
        difficulty = qdict["difficulty"][0]
        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dmail_address):
            if difficulty != dmail_address.keys[0].difficulty:
                dmail_address.keys[0].difficulty = difficulty
                return True
            else:
                return False

        dmail_address = yield from _process_dmail_address(
            dispatcher, processor, site_key=mbase32.decode(addr_enc), fetch_keys=True
        )

        dh = dhgroup14.DhGroup14()
        dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
        dh.generate_e()

        dms = dmail.DmailSite()
        root = dms.root
        root["ssm"] = "mdh-v1"
        root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))
        root["target"] = mbase32.encode(dmail_address.keys[0].target_key)
        root["difficulty"] = int(difficulty)

        private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

        de = dmail.DmailEngine(dispatcher.node.chord_engine.tasks, dispatcher.node.db)

        storing_nodes = yield from de.publish_dmail_site(private_key, dms)

        if storing_nodes:
            dispatcher.send_content(
                templates.dmail_addr_settings_edit_success_content[0].format(addr_enc, addr_enc[:32]).encode()
            )
        else:
            dispatcher.send_content(
                templates.dmail_addr_settings_edit_fail_content[0].format(addr_enc, addr_enc[:32]).encode()
            )
    else:
        dispatcher.send_error(errcode=400)
Esempio n. 27
0
def __serve_get(handler, rpath, done_event):
    if len(rpath) == len(s_dmail):
        handler._send_content(pages.dmail_page_content)
    else:
        req = rpath[len(s_dmail):]
        log.info("req=[{}].".format(req))
        if req == "/css":
            handler._send_content(\
                pages.dmail_css_content, content_type="text/css")
        elif req == "/address_list":
            handler._send_partial_content(
                pages.dmail_page_content__f1_start, True)

            site_keys = yield from _list_dmail_addresses(handler)

            for dbid, site_key in site_keys:
                site_key_enc = mbase32.encode(site_key)

                resp = """<span class="nowrap">[<a href="addr/{}">view</a>]"""\
                    """ {}</span><br/>"""\
                        .format(site_key_enc, site_key_enc)

                handler._send_partial_content(resp)

            handler._send_partial_content(pages.dmail_page_content__f1_end)
            handler._end_partial_content()
        elif req.startswith("/compose/form"):
            dest_addr_enc = req[14:] if len(req) > 14 else ""

            handler._send_partial_content(\
                pages.dmail_compose_dmail_form_start, True)

            site_keys = yield from _list_dmail_addresses(handler)

            for dbid, site_key in site_keys:
                site_key_enc = mbase32.encode(site_key)

                sender_element = """<option value="{}">{}</option>"""\
                    .format(dbid, site_key_enc)

                handler._send_partial_content(sender_element)

            handler._send_partial_content(\
                "<option value="">[Anonymous]</option>")

            handler._send_partial_content(\
                pages.dmail_compose_dmail_form_end.replace(\
                    b"${DEST_ADDR}", dest_addr_enc.encode()))

            handler._end_partial_content()
        elif req.startswith("/compose"):
            from_addr = req[9:] if len(req) > 9 else ""

            if from_addr:
                iframe_src = "../compose/form/{}".format(from_addr).encode()
            else:
                iframe_src = "compose/form".encode()

            content = pages.dmail_compose_dmail_content[0].replace(\
                    b"${IFRAME_SRC}", iframe_src)

            handler._send_content([content, None])
        elif req.startswith("/addr/view/"):
            addr_enc = req[11:]

            start = pages.dmail_addr_view_start.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS_SHORT}", addr_enc[:32].encode())

            handler._send_partial_content(start, True)

            handler._send_partial_content(pages.dmail_addr_view_end)
            handler._end_partial_content()
        elif req.startswith("/addr/settings/edit/publish?"):
            query = req[28:]

            qdict = urllib.parse.parse_qs(query, keep_blank_values=True)

            addr_enc = qdict["dmail_address"][0]
            difficulty = qdict["difficulty"][0]

            def processor(dmail_address):
                if difficulty != dmail_address.keys[0].difficulty:
                    dmail_address.keys[0].difficulty = difficulty
                    return True
                else:
                    return False

            dmail_address = yield from\
                _process_dmail_address(\
                    handler, mbase32.decode(addr_enc), processor)

            dh = dhgroup14.DhGroup14()
            dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
            dh.generate_e()

            dms = dmail.DmailSite()
            root = dms.root
            root["target"] =\
                mbase32.encode(dmail_address.keys[0].target_key)
            root["difficulty"] = int(difficulty)
            root["ssm"] = "mdh-v1"
            root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))

            private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

            r = yield from\
                handler.node.chord_engine.tasks.send_store_updateable_key(\
                    dms.export(), private_key,\
                    version=int(time.time()*1000), store_key=True)

            handler._send_content(\
                pages.dmail_addr_settings_edit_success_content[0]\
                    .format(addr_enc, addr_enc[:32]).encode())
        elif req.startswith("/addr/settings/edit/"):
            addr_enc = req[20:]

            dmail_address = yield from\
                _load_dmail_address(handler, mbase32.decode(addr_enc))

            content = pages.dmail_addr_settings_edit_content[0].replace(\
                b"${DIFFICULTY}",\
                str(dmail_address.keys[0].difficulty).encode())
            content = content.replace(\
                b"${DMAIL_ADDRESS_SHORT}", addr_enc[:32].encode())
            content = content.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            content = content.replace(\
                b"${PRIVATE_KEY}",\
                base58.encode(dmail_address.site_privatekey).encode())
            content = content.replace(\
                b"${X}", base58.encode(dmail_address.keys[0].x).encode())
            content = content.replace(\
                b"${TARGET_KEY}",\
                base58.encode(dmail_address.keys[0].target_key).encode())

            handler._send_content([content, None])
        elif req.startswith("/addr/settings/"):
            addr_enc = req[15:]

            content = pages.dmail_addr_settings_content[0].replace(\
                b"${IFRAME_SRC}",\
                "edit/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/addr/"):
            addr_enc = req[6:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing dmail address [{}].".format(addr_enc))

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "view/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/tag/view/list/"):
            params = req[15:]

            p0 = params.index('/')
            tag = params[:p0]
            addr_enc = params[p0+1:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing dmails with tag [{}] for address [{}]."\
                    .format(tag, addr_enc))

            start = pages.dmail_tag_view_list_start.replace(\
                b"${TAG_NAME}", tag.encode())
            #FIXME: This is getting inefficient now, maybe time for Flask or
            # something like it. Maybe we can use just it's template renderer.
            start = start.replace(b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS2}",\
                "{}...".format(addr_enc[:32]).encode())

            handler._send_partial_content(start, True)

            yield from\
                _list_dmails_for_tag(handler, mbase32.decode(addr_enc), tag)

            handler._send_partial_content(pages.dmail_tag_view_list_end)
            handler._end_partial_content()

        elif req.startswith("/tag/view/"):
            params = req[10:]

            content = pages.dmail_tag_view_content[0].replace(\
                b"${IFRAME_SRC}", "../list/{}".format(params).encode())

            handler._send_content(content)
        elif req.startswith("/scan/list/"):
            addr_enc = req[11:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing inbox for dmail address [{}]."\
                    .format(addr_enc))

            start = pages.dmail_inbox_start.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS2}", "{}...".format(addr_enc[:32]).encode())

            handler._send_partial_content(start, True)

            addr, significant_bits = mutil.decode_key(addr_enc)

            yield from _scan_new_dmails(handler, addr, significant_bits)

            handler._send_partial_content(pages.dmail_inbox_end)
            handler._end_partial_content()
        elif req.startswith("/scan/"):
            addr_enc = req[6:]

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "list/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/view/"):
            keys = req[12:]
            p0 = keys.index('/')
            dmail_addr_enc = keys[:p0]
            dmail_key_enc = keys[p0+1:]

            dmail_addr = mbase32.decode(dmail_addr_enc)
            dmail_key = mbase32.decode(dmail_key_enc)

            dm = yield from _load_dmail(handler, dmail_key)

            if dm:
                valid_sig = dm.sender_valid
            else:
                dm, valid_sig =\
                    yield from _fetch_dmail(handler, dmail_addr, dmail_key)

            dmail_text = _format_dmail(dm, valid_sig)

            handler._send_content(\
                dmail_text.encode(), content_type="text/plain")
        elif req.startswith("/fetch/panel/mark_as_read/"):
            req_data = req[26:]

            p0 = req_data.index('/')
            dmail_key_enc = req_data[p0+1:]
            dmail_key = mbase32.decode(dmail_key_enc)

            def processor(dmail):
                dmail.read = not dmail.read
                return True

            yield from _process_dmail_message(handler, dmail_key, processor)

            handler._send_204()
        elif req.startswith("/fetch/panel/trash/"):
            req_data = req[20:]

            p0 = req_data.index('/')
            dmail_key_enc = req_data[p0+1:]
            dmail_key = mbase32.decode(dmail_key_enc)

            def processor(dmail):
                dmail.hidden = not dmail.hidden
                return True

            yield from _process_dmail_message(handler, dmail_key, processor)

            handler._send_204()
        elif req.startswith("/fetch/panel/"):
            req_data = req[13:]

            content = pages.dmail_fetch_panel_content[0].replace(\
                b"${DMAIL_IDS}", req_data.encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/wrapper/"):
            req_data = req[15:]

            content = pages.dmail_fetch_wrapper[0].replace(\
                b"${IFRAME_SRC}",\
                "../../view/{}"\
                    .format(req_data).encode())
            #FIXME: This is getting inefficient now, maybe time for Flask or
            # something like it. Maybe we can use just it's template renderer.
            content = content.replace(\
                b"${IFRAME2_SRC}",\
                "../../panel/{}"\
                    .format(req_data).encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/"):
            req_data = req[7:]

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "../wrapper/{}".format(req_data).encode())

            handler._send_content([content, None])
        elif req == "/create_address":
            handler._send_content(pages.dmail_create_address_content)
        elif req == "/create_address/form":
            handler._send_content(pages.dmail_create_address_form_content)
        elif req.startswith("/create_address/make_it_so?"):
            query = req[27:]

            qdict = urllib.parse.parse_qs(query, keep_blank_values=True)

            prefix = qdict["prefix"][0]
            difficulty = int(qdict["difficulty"][0])

            log.info("prefix=[{}].".format(prefix))
            privkey, dmail_key, dms =\
                yield from _create_dmail_address(handler, prefix, difficulty)

            dmail_key_enc = mbase32.encode(dmail_key)

            handler._send_partial_content(pages.dmail_frame_start, True)
            handler._send_partial_content(b"SUCCESS<br/>")
            handler._send_partial_content(\
                """<p>New dmail address: <a href="../addr/{}">{}</a></p>"""\
                    .format(dmail_key_enc, dmail_key_enc).encode())
            handler._send_partial_content(pages.dmail_frame_end)
            handler._end_partial_content()
        else:
            handler._handle_error()