1import os
2import sys
3import re
4import platform
5import shutil
6from datetime import datetime
7from decimal import Decimal
8
9is_verbose = True
10
11btc = Decimal('1'+'0'*8)
12
13
14def to_btc(value):
15    return Decimal(value)/btc
16
17
18def set_verbosity(b):
19    global is_verbose
20    is_verbose = b
21
22
23def print_error(*args):
24    if not is_verbose:
25        return
26    args = [str(item) for item in args]
27    sys.stderr.write(" ".join(args) + "\n")
28    sys.stderr.flush()
29
30
31def print_msg(*args):
32    # Stringify args
33    args = [str(item) for item in args]
34    sys.stdout.write(" ".join(args) + "\n")
35    sys.stdout.flush()
36
37
38def print_json(obj):
39    import json
40    s = json.dumps(obj, sort_keys=True, indent=4)
41    sys.stdout.write(s + "\n")
42    sys.stdout.flush()
43
44
45def check_windows_wallet_migration():
46    if platform.release() != "XP":
47        path = os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
48        if os.path.exists(path):
49            if os.path.exists(os.path.join(os.environ["APPDATA"], "Electrum")):
50                print_msg("Two Electrum folders have been found, the " +
51                          "default Electrum location for Windows has " +
52                          "changed from %s to %s since Electrum 1.7, " +
53                          "please check your wallets and fix the problem " +
54                          "manually." %
55                          (os.environ["LOCALAPPDATA"], os.environ["APPDATA"]))
56                sys.exit()
57            try:
58                shutil.move(path, os.path.join(os.environ["APPDATA"]))
59                print_msg("Your wallet has been moved from %s to %s." %
60                          (os.environ["LOCALAPPDATA"], os.environ["APPDATA"]))
61            except:
62                print_msg("Failed to move your wallet.")
63
64
65def user_dir():
66    if "HOME" in os.environ:
67        return os.path.join(os.environ["HOME"], ".electrum")
68    elif "APPDATA" in os.environ:
69        return os.path.join(os.environ["APPDATA"], "Electrum")
70    elif "LOCALAPPDATA" in os.environ:
71        return os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
72    else:
73        return
74
75
76def appdata_dir():
77    """Find the path to the application data directory;
78       add an electrum folder and return path."""
79    if platform.system() == "Windows":
80        return os.path.join(os.environ["APPDATA"], "Electrum")
81    elif platform.system() == "Linux":
82        return os.path.join(sys.prefix, "share", "electrum")
83    elif (platform.system() == "Darwin" or
84          platform.system() == "DragonFly" or
85          platform.system() == "NetBSD"):
86        return "/Library/Application Support/Electrum"
87    else:
88        raise Exception("Unknown system")
89
90
91def get_resource_path(*args):
92    return os.path.join(".", *args)
93
94
95def local_data_dir():
96    """Return path to the data folder."""
97    assert sys.argv
98    prefix_path = os.path.dirname(sys.argv[0])
99    local_data = os.path.join(prefix_path, "data")
100    return local_data
101
102
103def format_satoshis(x, is_diff=False,
104                    num_zeros=0, decimal_point=8,
105                    whitespaces=False):
106    from decimal import Decimal
107    s = Decimal(x)
108    sign, digits, exp = s.as_tuple()
109    digits = map(str, digits)
110    while len(digits) < decimal_point + 1:
111        digits.insert(0, '0')
112    digits.insert(-decimal_point, '.')
113    s = ''.join(digits).rstrip('0')
114    if sign:
115        s = '-' + s
116    elif is_diff:
117        s = "+" + s
118
119    p = s.find('.')
120    s += "0" * (1 + num_zeros - (len(s) - p))
121    if whitespaces:
122        s += " " * (1 + decimal_point - (len(s) - p))
123        s = " " * (13 - decimal_point - (p)) + s
124    return s
125
126
127# Takes a timestamp and returns a string with the approximation of the age
128def age(from_date, since_date=None, target_tz=None, include_seconds=False):
129    if from_date is None:
130        return "Unknown"
131
132    from_date = datetime.fromtimestamp(from_date)
133    if since_date is None:
134        since_date = datetime.now(target_tz)
135
136    distance_in_time = since_date - from_date
137    distance_in_seconds = distance_in_time.days * 86400
138    distance_in_seconds += distance_in_time.seconds
139    distance_in_seconds = int(round(abs(distance_in_seconds)))
140    distance_in_minutes = int(round(distance_in_seconds/60))
141
142    if distance_in_minutes <= 1:
143        if include_seconds:
144            for remainder in [5, 10, 20]:
145                if distance_in_seconds < remainder:
146                    return "less than %s seconds ago" % remainder
147            if distance_in_seconds < 40:
148                return "half a minute ago"
149            elif distance_in_seconds < 60:
150                return "less than a minute ago"
151            else:
152                return "1 minute ago"
153        else:
154            if distance_in_minutes == 0:
155                return "less than a minute ago"
156            else:
157                return "1 minute ago"
158    elif distance_in_minutes < 45:
159        return "%s minutes ago" % distance_in_minutes
160    elif distance_in_minutes < 90:
161        return "about 1 hour ago"
162    elif distance_in_minutes < 1440:
163        return "about %d hours ago" % (round(distance_in_minutes / 60.0))
164    elif distance_in_minutes < 2880:
165        return "1 day ago"
166    elif distance_in_minutes < 43220:
167        return "%d days ago" % (round(distance_in_minutes / 1440))
168    elif distance_in_minutes < 86400:
169        return "about 1 month ago"
170    elif distance_in_minutes < 525600:
171        return "%d months ago" % (round(distance_in_minutes / 43200))
172    elif distance_in_minutes < 1051200:
173        return "about 1 year ago"
174    else:
175        return "over %d years ago" % (round(distance_in_minutes / 525600))
176
177# URL decode
178_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
179urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
180
181
182def parse_url(url):
183    o = url[8:].split('?')
184    address = o[0]
185    if len(o) > 1:
186        params = o[1].split('&')
187    else:
188        params = []
189
190    amount = label = message = signature = identity = ''
191    for p in params:
192        k, v = p.split('=')
193        uv = urldecode(v)
194        if k == 'amount':
195            amount = uv
196        elif k == 'message':
197            message = uv
198        elif k == 'label':
199            label = uv
200        elif k == 'signature':
201            identity, signature = uv.split(':')
202            url = url.replace('&%s=%s' % (k, v), '')
203        else:
204            print k, v
205
206    return address, amount, label, message, signature, identity, url
207