def track_data(name, data): """ Track arbitrary data. Calling this function will generate a data tracking event. :param name: The identifier of the data. :type name: str :param data: Arbitrary data, must be compatible with JSON. :type data: dict, list, str, int, float, None """ event = { "type": "data", "time": int(time.time()), "timezone_offset": get_utc_offset(), "os_version": OS_VERSION, "cpu_id": CPU_ID, "token": TOKEN, "name": str(name), "data": data } try: af = open_locked(tracker_events_file, "a") except IOError as e: logger.error('Error opening tracker events file {}'.format(e)) else: with af: af.write(json.dumps(event) + "\n") if 'SUDO_USER' in os.environ: chown_path(tracker_events_file)
def set_locale_param(param, locale, skip_check=False): if not skip_check and not is_locale_installed(locale): install_locale(locale) param_found = False new_param_line = 'export {}={}'.format(param, locale) new_config_file = [] if os.path.exists(XSESSION_RC_FILE): xsession_file = read_file_contents_as_lines(XSESSION_RC_FILE) for line in xsession_file: if param in line: line = new_param_line param_found = True new_config_file.append(line) if not param_found: new_config_file.append(new_param_line) with open(XSESSION_RC_FILE, 'w') as conf_file: conf_file.write('\n'.join(new_config_file)) chown_path(XSESSION_RC_FILE)
def session_end(session_file): if not os.path.exists(session_file): msg = "Someone removed the tracker file, the runtime of this " + \ "app will not be logged" logger.warn(msg) return try: rf = open_locked(session_file, "r") except IOError as e: logger.error('Error opening the tracker session file {}'.format(e)) else: with rf: data = json.load(rf) data["elapsed"] = int(time.time()) - data["started"] data["finished"] = True try: wf = open(session_file, "w") except IOError as e: logger.error( 'Error opening the tracker session file {}'.format(e)) else: with wf: json.dump(data, wf) if 'SUDO_USER' in os.environ: chown_path(data)
def session_log(name, started, length): """ Log a session that was tracked outside of the tracker. :param name: The identifier of the session. :type name: str :param started: When was the session started (UTC unix timestamp). :type started: int :param length: Length of the session in seconds. :param started: int """ try: af = open_locked(tracker_events_file, 'a') except IOError as e: logger.error('Error while opening events file'.format(e)) else: with af: session = { "name": name, "started": int(started), "elapsed": int(length) } event = get_session_event(session) af.write(json.dumps(event) + "\n") if 'SUDO_USER' in os.environ: chown_path(tracker_events_file)
def generate_tracker_token(): """ Generating the token is a simple md5hash of the current time. The token is saved to the `tracker_token_file`. :returns: The token. :rtype: str """ token = hashlib.md5(str(time.time())).hexdigest() ensure_dir(tracker_dir) try: f = open_locked(tracker_token_file, "w") except IOError as e: logger.error( 'Error opening tracker token file (generate) {}'.format(e)) else: with f: f.write(token) if 'SUDO_USER' in os.environ: chown_path(tracker_token_file) # Make sure that the events file exist try: f = open(tracker_events_file, 'a') except IOError as e: logger.error('Error opening tracker events file {}'.format(e)) else: f.close() if 'SUDO_USER' in os.environ: chown_path(tracker_events_file) return token
def session_start(name, pid=None): if not pid: pid = os.getpid() pid = int(pid) data = { "pid": pid, "name": name, "started": int(time.time()), "elapsed": 0, "finished": False } path = get_session_file_path(data['name'], data['pid']) try: f = open_locked(path, "w") except IOError as e: logger.error('Error opening tracker session file {}'.format(e)) else: with f: json.dump(data, f) if 'SUDO_USER' in os.environ: chown_path(path) return path
def set_locale_param(param, locale, skip_check=False): # FIXME: Don't use the .xsessionrc file to set the locale XSESSION_RC_FILE = os.path.join(get_home_by_username(get_user_unsudoed()), '.xsessionrc') if not skip_check and not is_locale_installed(locale): install_locale(locale) param_found = False new_param_line = 'export {}={}'.format(param, locale) new_config_file = [] if os.path.exists(XSESSION_RC_FILE): xsession_file = read_file_contents_as_lines(XSESSION_RC_FILE) for line in xsession_file: if param in line: line = new_param_line param_found = True new_config_file.append(line) if not param_found: new_config_file.append(new_param_line) with open(XSESSION_RC_FILE, 'w') as conf_file: conf_file.write('\n'.join(new_config_file)) chown_path(XSESSION_RC_FILE)
def session_start(name, pid=None): if not pid: pid = os.getpid() pid = int(pid) data = { 'pid': pid, 'name': name, 'started': int(time.time()), 'elapsed': 0, 'app_session_id': str(uuid5(uuid1(), name + str(pid))), 'finished': False, 'token-system': TOKEN } path = get_session_file_path(data['name'], data['pid']) try: f = open_locked(path, 'w') except IOError as e: logger.error("Error opening tracker session file {}".format(e)) else: with f: json.dump(data, f) if 'SUDO_USER' in os.environ: chown_path(path) return path
def clear_tracker_events(old_only=True): """ Truncate the events file, removing all the cached data. :param old_only: Don't remove data from the current boot. :type old_only: boolean """ try: rf = open_locked(tracker_events_file, "r") except IOError as e: logger.error('Error opening tracking events file {}'.format(e)) else: with rf: events = [] for event_line in rf.readlines(): try: event = json.loads(event_line) if 'token' in event and event['token'] == TOKEN: events.append(event_line) except: logger.warn("Found a corrupted event, skipping.") with open(tracker_events_file, "w") as wf: for event_line in events: wf.write(event_line) if 'SUDO_USER' in os.environ: chown_path(tracker_events_file)
def download_online_badges(self): profile = load_profile() if "kanoworld_id" in profile: user_id = profile["kanoworld_id"] else: return False, "Profile not registered!" success, text, data = request_wrapper("get", "/users/{}".format(user_id), session=self.session) if not success: return False, text if "user" not in data: return False, "Corrupt response (the 'user' key not found)" if "profile" not in data["user"]: return False, "Corrupt response (the 'user.profile' key not found)" if "badges" not in data["user"]["profile"]: msg = "Corrupt response (the 'user.profile.badges' key not found)" return False, msg online_badges_data = {} ensure_dir(online_badges_dir) badges = data["user"]["profile"]["badges"] for badge in badges: if "assigned" not in badge or not badge["assigned"]: continue if "image_url" not in badge: return False, "Couldn't find an image for the badge" image_loc = os.path.join(online_badges_dir, "{}.png".format(badge["id"])) download_url(badge["image_url"], image_loc) online_badges_data[badge["id"]] = { "achieved": True, "bg_color": badge["bg_color"].replace("#", ""), "desc_locked": badge["desc_locked"], "desc_unlocked": badge["desc_unlocked"], "title": badge["title"], } try: may_write = True txt = None f = open(online_badges_file, "w") except IOError as e: may_write = False txt = "Error opening badges file {}".format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if "SUDO_USER" in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
def set_setting(variable, value): if username == 'root': return logger.debug('config_file / set_setting: {} {}'.format(variable, value)) data = read_json(settings_file) if not data: data = dict() data[variable] = value write_json(settings_file, data) chown_path(settings_file)
def set_user_cookies(enabled=None, username=None): if enabled is None: enabled = get_parental_level() >= 2 if username is None: return # The cookie enables/disables safety mode in YouTube (Midori) # The .db files are located in /usr/share/kano-video homedir = "/home/{}".format(username) if not os.path.isdir(homedir): logger.error("Could not access user home dir: {}".format(homedir)) return # Browser: Cookie needs to be copied to /home/USERNAME/.config/midori midori_cookie_path = '{}/{}'.format(homedir, midori_cookie) if os.path.exists(browser_safe_cookie) and \ os.path.exists(browser_nosafe_cookie) and \ os.path.exists(midori_cookie_path): if enabled: logger.debug('Enabling Browser Safety mode for browser on user {}'.format(username)) browser_cookie = browser_safe_cookie else: logger.debug('Disabling Browser Safety mode for browser on user {}'.format(username)) browser_cookie = browser_nosafe_cookie # Copy cookie for this user shutil.copy(browser_cookie, midori_cookie_path) # Set correct permissions on file chown_path('{}/cookies.db'.format(midori_cookie_path), username, username) # YT: copy yo /home/USERNAME/.config/midori/youtube (kano-video-browser) youtube_cookie_path = '{}/{}'.format(homedir, youtube_cookie) if os.path.exists(youtube_safe_cookie) and \ os.path.exists(youtube_nosafe_cookie) and \ os.path.exists(youtube_cookie_path): if enabled: logger.debug('Enabling YouTube Safety mode for kano-video-browser on user {}'.format(username)) yt_cookie = youtube_safe_cookie else: logger.debug('Disabling YT Safety mode for kano-video-browser on user {}'.format(username)) yt_cookie = youtube_nosafe_cookie # Copy cookie for this user shutil.copy(yt_cookie, youtube_cookie_path) # Set correct permissions on file chown_path('{}/cookies.db'.format(youtube_cookie_path), username, username)
def track_action(name): """ Trigger an action tracking event. :param name: The identifier of the action. :type name: str """ try: af = open_locked(tracker_events_file, 'a') except IOError as e: logger.error('Error opening tracker events file {}'.format(e)) else: with af: event = get_action_event(name) af.write(json.dumps(event) + "\n") if 'SUDO_USER' in os.environ: chown_path(tracker_events_file)
def save_app_state_variable_all_users(app, variable, value): if os.environ['LOGNAME'] != 'root': logger.error( "Error: save_app_state_variable_all_users must be executed with root privileges" ) return users = get_all_users() for user in users: dir_path = os.path.join("/home", user, ".kanoprofile", "apps", app) if not os.path.exists(dir_path): os.makedirs(dir_path) chown_path(dir_path, user, user) state_path = os.path.join(dir_path, "state.json") data = {variable: value} data['save_date'] = get_date_now() write_json(state_path, data) chown_path(state_path, user, user)
def save_app_state_variable_all_users(app, variable, value): if os.environ['LOGNAME'] != 'root': logger.error("Error: save_app_state_variable_all_users must be executed with root privileges") return users = get_all_users() for user in users: dir_path = os.path.join( "/home", user, ".kanoprofile", "apps", app ) if not os.path.exists(dir_path): os.makedirs(dir_path) chown_path(dir_path, user, user) state_path = os.path.join(dir_path, "state.json") data = {variable: value} data['save_date'] = get_date_now() write_json(state_path, data) chown_path(state_path, user, user)
def save_app_state(app_name, data): """ Save a state of an application to the user's Kano profile. :param app_name: The application that this data are associated with. :type app_name: str :param data: The data to be stored. :type data: dict """ logger.debug('save_app_state {}'.format(app_name)) app_state_file = get_app_state_file(app_name) data['save_date'] = get_date_now() ensure_dir(get_app_dir(app_name)) write_json(app_state_file, data) if 'SUDO_USER' in os.environ: chown_path(kanoprofile_dir) chown_path(apps_dir) chown_path(get_app_dir(app_name)) chown_path(app_state_file)
def save_app_state(app_name, data): """ Save a state of an application to the user's Kano profile. :param app_name: The application that this data are associated with. :type app_name: str :param data: The data to be stored. :type data: dict """ logger.debug("save_app_state {}".format(app_name)) app_state_file = get_app_state_file(app_name) data['save_date'] = get_date_now() ensure_dir(get_app_dir(app_name)) write_json(app_state_file, data) if 'SUDO_USER' in os.environ: chown_path(kanoprofile_dir) chown_path(apps_dir) chown_path(get_app_dir(app_name)) chown_path(app_state_file)
def save_profile(data): ''' Write profile data to file :param data: JSON serialisable data about the profile ''' logger.debug('save_profile') data.pop('cpu_id', None) data.pop('mac_addr', None) data['save_date'] = get_date_now() ensure_dir(profile_dir) write_json(profile_file, data) if 'SUDO_USER' in os.environ: chown_path(kanoprofile_dir) chown_path(profile_dir) chown_path(profile_file) if os.path.exists('/usr/bin/kdesk') and not is_running('kano-sync'): logger.info('refreshing kdesk from save_profile') run_bg('kdesk -a profile')
def save_profile(data, skip_kdesk_refresh=False): ''' Write profile data to file :param data: JSON serialisable data about the profile ''' logger.debug('save_profile') data.pop('cpu_id', None) data.pop('mac_addr', None) data['save_date'] = get_date_now() ensure_dir(profile_dir) write_json(profile_file, data) if 'SUDO_USER' in os.environ: chown_path(kanoprofile_dir) chown_path(profile_dir) chown_path(profile_file) if (not skip_kdesk_refresh and os.path.exists('/usr/bin/kdesk') and not is_running('kano-sync')): logger.info("refreshing kdesk from save_profile") run_bg('kdesk -a profile')
def update_folder_from_skel(user_name): logger.info("Updating home folder of user: {}".format(user_name)) src_dir = '/etc/skel' dst_dir = os.path.join('/home', user_name) dirlinks = [] filelinks = [] files = [] for root, dirs, filenames in os.walk(src_dir): for d in dirs: path_full = os.path.join(root, d) if os.path.islink(path_full): dirlinks.append(path_full) for f in filenames: path_full = os.path.join(root, f) if os.path.islink(path_full): filelinks.append(path_full) else: files.append(path_full) for path_full in dirlinks + filelinks + files: path_rel = os.path.relpath(path_full, src_dir) dir_path_rel = os.path.dirname(path_rel) dst_path = os.path.join(dst_dir, path_rel) dir_dst_path = os.path.join(dst_dir, dir_path_rel) # print 'path_full', path_full # print 'path_rel', path_rel # print 'dir_path_rel', dir_path_rel # print 'dst_path', dst_path # print 'dir_dst_path', dir_dst_path # print if os.path.exists(dst_path): if os.path.islink(dst_path): logger.info("removing link: {}".format(dst_path)) os.unlink(dst_path) elif os.path.isdir(dst_path): logger.info("removing dir: {}".format(dst_path)) shutil.rmtree(dst_path) elif os.path.isfile(dst_path): logger.info("removing file: {}".format(dst_path)) os.remove(dst_path) # make sure that destination directory exists if os.path.exists(dir_dst_path): if not os.path.isdir(dir_dst_path): os.remove(dir_dst_path) else: logger.info("making needed dir: {}".format(dir_dst_path)) os.makedirs(dir_dst_path) chown_path(dir_dst_path, user=user_name, group=user_name) # creating links if os.path.islink(path_full): linkto = os.readlink(path_full) msg = "creating link {} -> {}".format(dst_path, linkto) logger.info(msg) os.symlink(linkto, dst_path) chown_path(dst_path, user=user_name, group=user_name) elif os.path.isfile(path_full): msg = "copying file {} -> {}".format(path_full, dst_path) logger.info(msg) shutil.copy(path_full, dst_path) chown_path(dst_path, user=user_name, group=user_name)
def download_online_badges(self): profile = load_profile() if 'kanoworld_id' in profile: user_id = profile['kanoworld_id'] else: return False, 'Profile not registered!' success, text, data = request_wrapper('get', '/users/{}'.format(user_id), session=self.session) if not success: return False, text if 'user' not in data: return False, _("Corrupt response (the 'user' key not found)") if 'profile' not in data['user']: return False, _( "Corrupt response (the 'user.profile' key not found)") if 'badges' not in data['user']['profile']: return False, _( "Corrupt response (the 'user.profile.badges' key not found)") online_badges_data = {} ensure_dir(online_badges_dir) badges = data['user']['profile']['badges'] for badge in badges: if 'assigned' not in badge or not badge['assigned']: continue if 'image_url' not in badge: return False, _("Couldn't find an image for the badge") image_loc = os.path.join(online_badges_dir, "{}.png".format(badge['id'])) download_url(badge['image_url'], image_loc) online_badges_data[badge['id']] = { 'achieved': True, 'bg_color': badge['bg_color'].replace("#", ""), 'desc_locked': badge['desc_locked'], 'desc_unlocked': badge['desc_unlocked'], 'title': badge['title'] } try: may_write = True txt = None f = open(online_badges_file, 'w') except IOError as e: may_write = False txt = 'Error opening badges file {}'.format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if 'SUDO_USER' in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
def download_online_badges(self): profile = load_profile() if 'kanoworld_id' in profile: user_id = profile['kanoworld_id'] else: return False, 'Profile not registered!' success, text, data = request_wrapper( 'get', '/users/{}'.format(user_id), session=self.session ) if not success: return False, text if 'user' not in data: return False, _("Corrupt response (the 'user' key not found)") if 'profile' not in data['user']: return False, _("Corrupt response (the 'user.profile' key not found)") if 'badges' not in data['user']['profile']: return False, _("Corrupt response (the 'user.profile.badges' key not found)") online_badges_data = {} ensure_dir(online_badges_dir) badges = data['user']['profile']['badges'] for badge in badges: if 'assigned' not in badge or not badge['assigned']: continue if 'image_url' not in badge: return False, _("Couldn't find an image for the badge") image_loc = os.path.join(online_badges_dir, "{}.png".format(badge['id'])) download_url(badge['image_url'], image_loc) online_badges_data[badge['id']] = { 'achieved': True, 'bg_color': badge['bg_color'].replace("#", ""), 'desc_locked': badge['desc_locked'], 'desc_unlocked': badge['desc_unlocked'], 'title': badge['title'] } try: may_write = True txt = None f = open(online_badges_file, 'w') except IOError as e: may_write = False txt = 'Error opening badges file {}'.format(str(e)) else: with f: f.write(json.dumps(online_badges_data)) if 'SUDO_USER' in os.environ: chown_path(online_badges_dir) chown_path(online_badges_file) return may_write, txt
import re import shutil from kano.utils import ensure_dir, get_user_unsudoed, read_json, write_json, chown_path from kano.logging import logger from kano_settings.common import settings_dir from kano.utils import is_model_2_b USER = None USER_ID = None username = get_user_unsudoed() if username != 'root': if os.path.exists(settings_dir) and os.path.isfile(settings_dir): os.rename(settings_dir, settings_dir + '.bak') ensure_dir(settings_dir) chown_path(settings_dir) settings_file = os.path.join(settings_dir, 'settings') defaults = { 'pi1': { 'Keyboard-continent-index': 1, 'Keyboard-country-index': 21, 'Keyboard-variant-index': 0, 'Keyboard-continent-human': 'america', 'Keyboard-country-human': 'United States', 'Keyboard-variant-human': 'Generic', 'Audio': 'Analogue', 'Wifi': '', 'Wifi-connection-attempted': False, 'Overclocking': 'High', 'Mouse': 'Normal',