aboutsummaryrefslogtreecommitdiffstats
path: root/mitmproxy/addons/export.py
blob: a7152ae414e7274ae9bc94806116d7f4fb0b8a3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import typing
import shlex

from mitmproxy import ctx
from mitmproxy import command
from mitmproxy import flow
from mitmproxy import exceptions
from mitmproxy.utils import strutils
from mitmproxy.net.http.http1 import assemble
import mitmproxy.types

import pyperclip


def raise_if_missing_request(f: flow.Flow) -> None:
    if not hasattr(f, "request"):
        raise exceptions.CommandError("Can't export flow with no request.")


def curl_command(f: flow.Flow) -> str:
    raise_if_missing_request(f)
    args = ["curl"]
    request = f.request.copy()  # type: ignore
    request.decode(strict=False)
    for k, v in request.headers.items(multi=True):
        args += ["-H", "%s:%s" % (k, v)]
    if request.method != "GET":
        args += ["-X", request.method]
    args.append(request.url)
    if request.content:
        try:
            content = strutils.always_str(request.content)
        except UnicodeDecodeError:
            # shlex.quote doesn't support a bytes object
            # see https://github.com/python/cpython/pull/10871
            raise exceptions.CommandError("Request content must be valid unicode")
        args += ["--data-binary", content]
    return ' '.join(shlex.quote(arg) for arg in args)


def httpie_command(f: flow.Flow) -> str:
    raise_if_missing_request(f)
    request = f.request.copy()  # type: ignore
    request.decode(strict=False)
    args = ["http", request.method, request.url]
    for k, v in request.headers.items(multi=True):
        args.append("%s:%s" % (k, v))
    cmd = ' '.join(shlex.quote(arg) for arg in args)
    if request.content:
        try:
            content = strutils.always_str(request.content)
        except UnicodeDecodeError:
            # shlex.quote doesn't support a bytes object
            # see https://github.com/python/cpython/pull/10871
            raise exceptions.CommandError("Request content must be valid unicode")
        cmd += " <<< " + shlex.quote(content)
    return cmd


def raw(f: flow.Flow) -> bytes:
    raise_if_missing_request(f)
    return assemble.assemble_request(f.request)  # type: ignore


formats = dict(
    curl = curl_command,
    httpie = httpie_command,
    raw = raw,
)


class Export():
    @command.command("export.formats")
    def formats(self) -> typing.Sequence[str]:
        """
            Return a list of the supported export formats.
        """
        return list(sorted(formats.keys()))

    @command.command("export.file")
    def file(self, fmt: str, f: flow.Flow, path: mitmproxy.types.Path) -> None:
        """
            Export a flow to path.
        """
        if fmt not in formats:
            raise exceptions.CommandError("No such export format: %s" % fmt)
        func: typing.Any = formats[fmt]
        v = func(f)
        try:
            with open(path, "wb") as fp:
                if isinstance(v, bytes):
                    fp.write(v)
                else:
                    fp.write(v.encode("utf-8"))
        except IOError as e:
            ctx.log.error(str(e))

    @command.command("export.clip")
    def clip(self, fmt: str, f: flow.Flow) -> None:
        """
            Export a flow to the system clipboard.
        """
        if fmt not in formats:
            raise exceptions.CommandError("No such export format: %s" % fmt)
        func: typing.Any = formats[fmt]
        v = strutils.always_str(func(f))
        try:
            pyperclip.copy(v)
        except pyperclip.PyperclipException as e:
            ctx.log.error(str(e))