1import os
2import os.path as path
3import sys
4import uuid
5import configparser
6
7
8class ConfigError(Exception):
9    pass
10
11
12DEFAULT_API_URL = 'https://asciinema.org'
13DEFAULT_RECORD_ENV = 'SHELL,TERM'
14
15
16class Config:
17
18    def __init__(self, config_home, env=None):
19        self.config_home = config_home
20        self.config_file_path = path.join(config_home, "config")
21        self.install_id_path = path.join(self.config_home, 'install-id')
22        self.config = configparser.ConfigParser()
23        self.config.read(self.config_file_path)
24        self.env = env if env is not None else os.environ
25
26    def upgrade(self):
27        try:
28            self.install_id
29        except ConfigError:
30            id = self.__api_token() or self.__user_token() or self.__gen_install_id()
31            self.__save_install_id(id)
32
33            items = {name: dict(section) for (name, section) in self.config.items()}
34            if items == {'DEFAULT': {}, 'api': {'token': id}} or items == {'DEFAULT': {}, 'user': {'token': id}}:
35                os.remove(self.config_file_path)
36
37        if self.env.get('ASCIINEMA_API_TOKEN'):
38            raise ConfigError('ASCIINEMA_API_TOKEN variable is no longer supported, please use ASCIINEMA_INSTALL_ID instead')
39
40    def __read_install_id(self):
41        p = self.install_id_path
42        if path.isfile(p):
43            with open(p, 'r') as f:
44                return f.read().strip()
45
46    def __gen_install_id(self):
47        return str(uuid.uuid4())
48
49    def __save_install_id(self, id):
50        self.__create_config_home()
51
52        with open(self.install_id_path, 'w') as f:
53            f.write(id)
54
55    def __create_config_home(self):
56        if not path.exists(self.config_home):
57            os.makedirs(self.config_home)
58
59    def __api_token(self):
60        try:
61            return self.config.get('api', 'token')
62        except (configparser.NoOptionError, configparser.NoSectionError):
63            pass
64
65    def __user_token(self):
66        try:
67            return self.config.get('user', 'token')
68        except (configparser.NoOptionError, configparser.NoSectionError):
69            pass
70
71    @property
72    def install_id(self):
73        id = self.env.get('ASCIINEMA_INSTALL_ID') or self.__read_install_id()
74
75        if id:
76            return id
77        else:
78            raise ConfigError('no install ID found')
79
80    @property
81    def api_url(self):
82        return self.env.get(
83            'ASCIINEMA_API_URL',
84            self.config.get('api', 'url', fallback=DEFAULT_API_URL)
85        )
86
87    @property
88    def record_stdin(self):
89        return self.config.getboolean('record', 'stdin', fallback=False)
90
91    @property
92    def record_command(self):
93        return self.config.get('record', 'command', fallback=None)
94
95    @property
96    def record_env(self):
97        return self.config.get('record', 'env', fallback=DEFAULT_RECORD_ENV)
98
99    @property
100    def record_idle_time_limit(self):
101        fallback = self.config.getfloat('record', 'maxwait', fallback=None)  # pre 2.0
102        return self.config.getfloat('record', 'idle_time_limit', fallback=fallback)
103
104    @property
105    def record_yes(self):
106        return self.config.getboolean('record', 'yes', fallback=False)
107
108    @property
109    def record_quiet(self):
110        return self.config.getboolean('record', 'quiet', fallback=False)
111
112    @property
113    def play_idle_time_limit(self):
114        fallback = self.config.getfloat('play', 'maxwait', fallback=None)  # pre 2.0
115        return self.config.getfloat('play', 'idle_time_limit', fallback=fallback)
116
117    @property
118    def play_speed(self):
119        return self.config.getfloat('play', 'speed', fallback=1.0)
120
121
122def get_config_home(env=os.environ):
123    env_asciinema_config_home = env.get("ASCIINEMA_CONFIG_HOME")
124    env_xdg_config_home = env.get("XDG_CONFIG_HOME")
125    env_home = env.get("HOME")
126
127    config_home = None
128
129    if env_asciinema_config_home:
130        config_home = env_asciinema_config_home
131    elif env_xdg_config_home:
132        config_home = path.join(env_xdg_config_home, "asciinema")
133    elif env_home:
134        if path.isfile(path.join(env_home, ".asciinema", "config")):
135            # location for versions < 1.1
136            config_home = path.join(env_home, ".asciinema")
137        else:
138            config_home = path.join(env_home, ".config", "asciinema")
139    else:
140        raise Exception("need $HOME or $XDG_CONFIG_HOME or $ASCIINEMA_CONFIG_HOME")
141
142    return config_home
143
144
145def load(env=os.environ):
146    config = Config(get_config_home(env), env)
147    config.upgrade()
148    return config
149