extra/src/lib/extra.c

541 lines
13 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include "extra.h"
#include "jsmn/jsmn.h"
#include "extra_private.h"
static int _extra_init = 0;
static Ecore_Event_Handler *_data;
static Ecore_Event_Handler *_complete;
int _extra_lib_log_dom = -1;
Eina_List *_theme_list;
#define sec_strdup(v) v ? eina_strbuf_string_steal(v) : NULL
void _extra_theme_cache_load();
#define PREVIEW_DOWNLOAD 1
#define THEME_DOWNLOAD 2
typedef struct {
Extra_Theme theme;
char state; //indicates if some downloads are in progress
} Extra_Theme_Private;
typedef struct {
Extra_Theme_Private *theme;
Extra_Progress *progress;
char nand_mask; //will be applied to the status char of the theme
} Extra_Download_Job;
static void
_extra_theme_add(Eina_Strbuf *id, Eina_Strbuf *name,
Eina_Strbuf *author, Eina_Strbuf *description,
int version)
{
Extra_Theme_Private *theme;
theme = malloc(sizeof(*theme));
theme->theme.id = sec_strdup(id);
theme->theme.name = sec_strdup(name);
theme->theme.author = sec_strdup(author);
theme->theme.description = sec_strdup(description);
theme->theme.version = version;
theme->state = 0;
_theme_list = eina_list_append(_theme_list, theme);
}
EAPI int
extra_init(void)
{
_extra_init++;
if (_extra_init > 1) return _extra_init;
eina_init();
_extra_lib_log_dom = eina_log_domain_register("extra", EINA_COLOR_CYAN);
if (_extra_lib_log_dom < 0)
{
EINA_LOG_ERR("extra can not create its log domain.");
goto shutdown_eina;
}
//we are putting stuff into <cachedir>/extra/ lets make sure its created
{
Eina_Strbuf *buf;
buf = eina_strbuf_new();
eina_strbuf_append(buf, efreet_cache_home_get());
eina_strbuf_append(buf, "/extra/");
ecore_file_mkdir(eina_strbuf_string_get(buf));
eina_strbuf_free(buf);
}
// Put here your initialization logic of your library
_extra_theme_cache_load();
eina_log_timing(_extra_lib_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT);
return _extra_init;
shutdown_eina:
eina_shutdown();
_extra_init--;
return _extra_init;
}
EAPI int
extra_shutdown(void)
{
_extra_init--;
if (_extra_init != 0) return _extra_init;
eina_log_timing(_extra_lib_log_dom,
EINA_LOG_STATE_START,
EINA_LOG_STATE_SHUTDOWN);
// Put here your shutdown logic
eina_log_domain_unregister(_extra_lib_log_dom);
_extra_lib_log_dom = -1;
eina_shutdown();
return _extra_init;
}
static Eina_Bool
_url_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event_info)
{
Ecore_Con_Event_Url_Data *url_data = event_info;
Eina_Strbuf *buf = ecore_con_url_data_get(url_data->url_con);
eina_strbuf_append_n(buf, (char*)url_data->data, url_data->size);
return EINA_TRUE;
}
static int
_string_tuple_get(Eina_Strbuf *c, jsmntok_t *array, int i, Eina_Strbuf **name, Eina_Strbuf **value)
{
if (array[i].type != JSMN_STRING || array[i].size != 1)
{
printf("expected string type with children\n");
return 0;
}
*name = eina_strbuf_substr_get(c, array[i].start, array[i].end - array[i].start);
if ((array[i + 1].type != JSMN_STRING && array[i + 1].type != JSMN_PRIMITIVE) || array[i + 1].size != 0)
{
printf("Expected string type without children\n");
return 0;
}
*value = eina_strbuf_substr_get(c, array[i + 1].start, array[i + 1].end - array[i + 1].start);
return 2;
}
static Eina_Bool
_fill_themes(Eina_Strbuf *buf)
{
jsmn_parser parser;
jsmntok_t parts[201];
int n, c = 0;
jsmn_init(&parser);
const char *string = eina_strbuf_string_get(buf);
n = jsmn_parse(&parser, string, strlen(string), parts, 201);
if (n == 0)
{
printf("No themes received\n");
return EINA_FALSE;
}
if (parts[0].type != JSMN_OBJECT)
{
printf("Root node should be a object\n");
return EINA_FALSE;
}
c += 1;
eina_list_free(_theme_list);
_theme_list = NULL;
for (int i = 0; i < parts[0].size; ++i)
{
Eina_Strbuf *id = NULL, *name = NULL, *version = NULL, *description = NULL, *author = NULL;
int versionNumb;
//expect string object tuple
if (parts[c].type != JSMN_STRING || parts[c].size != 1)
{
printf("Expected String type with one child\n");
return EINA_FALSE;
}
c += 1;
if (parts[c].type != JSMN_OBJECT || parts[c].size <= 0)
{
printf("Expected Object type with more than 0 children\n");
return EINA_FALSE;
}
int max = parts[c].size;
c+=1;
for (int i2 = 0; i2 < max; ++i2)
{
Eina_Strbuf *value = NULL, *property = NULL;
const char *v;
int j = _string_tuple_get(buf, parts, c, &value, &property);
if (!j) return EINA_FALSE;
c += j;
v = eina_strbuf_string_steal(value);
if (!strcmp(v, "description"))
description = property;
else if (!strcmp(v, "author"))
author = property;
else if (!strcmp(v, "name"))
name = property;
else if (!strcmp(v, "version"))
version = property;
else if (!strcmp(v, "theme_id"))
id = property;
else
eina_strbuf_free(property);
eina_strbuf_free(value);
}
versionNumb = atoi(eina_strbuf_string_steal(version));
_extra_theme_add(id, name, author, description, versionNumb);
eina_strbuf_free(id);
eina_strbuf_free(name);
eina_strbuf_free(author);
eina_strbuf_free(description);
eina_strbuf_free(version);
}
return EINA_TRUE;
}
static char *
_theme_cache_path_get()
{
char *path;
path = malloc(PATH_MAX * sizeof(char));
sprintf(path, "%s/%s/%s.json", efreet_cache_home_get(), PACKAGE_NAME, "themes");
return path;
}
static Eina_Bool
_url_complete_cb(void *data, int type EINA_UNUSED, void *event_info)
{
Extra_Progress *progress = data;;
Ecore_Con_Event_Url_Complete *complete = event_info;
Eina_Strbuf *buf;
Eina_Bool parsed;
buf = ecore_con_url_data_get(complete->url_con);
parsed = _fill_themes(buf);
if (parsed)
{
FILE *cache;
char *cache_path = _theme_cache_path_get();
const char *content = eina_strbuf_string_get(buf);
cache = fopen(cache_path, "w+");
fprintf(cache, "%s", content);
fclose(cache);
free(cache_path);
}
if (progress->done_cb)
progress->done_cb();
ecore_event_handler_del(_data);
ecore_event_handler_del(_complete);
return EINA_TRUE;
}
EAPI void
extra_sync(Extra_Progress *progress)
{
Ecore_Con_Url *url;
url = ecore_con_url_custom_new("http://" HOSTNAME "/v1/themes/", "GET");
ecore_con_url_additional_header_add(url, "Accept", "text/json");
ecore_con_url_data_set(url, eina_strbuf_new());
_data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, _url_data_cb, NULL);
_complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _url_complete_cb, progress);
ecore_con_url_get(url);
}
EAPI Eina_List *
extra_themes_list(void)
{
return _theme_list;
}
EAPI Extra_Theme *extra_theme_get(const char *id)
{
Extra_Theme *theme;
Eina_List *item;
EINA_LIST_FOREACH(extra_themes_list(), item, theme)
if (!strcmp(id, theme->id))
return theme;
return NULL;
}
EAPI Eina_Bool
extra_theme_installed(Extra_Theme *theme)
{
char *path;
Eina_Bool exists;
EINA_SAFETY_ON_NULL_RETURN_VAL(theme, EINA_FALSE);
path = extra_theme_install_path_get(theme);
exists = ecore_file_exists(path);
free(path);
return exists;
}
EAPI char *
extra_theme_install_path_get(Extra_Theme *theme)
{
Eina_Strbuf *buf;
char *path;
EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
buf = eina_strbuf_new();
eina_strbuf_append(buf, elm_theme_user_dir_get());
eina_strbuf_append_printf(buf, "/%s-%d.edj", theme->id, theme->version);
path = eina_strbuf_string_steal(buf);
eina_strbuf_free(buf);
return path;
}
static void
_download_complete_cb(void *data, const char *file EINA_UNUSED, int status EINA_UNUSED)
{
Extra_Download_Job *job = data;
job->theme->state &= (~job->nand_mask);
if (job->progress->done_cb)
job->progress->done_cb();
}
static int
_download_progress_cb(void *data EINA_UNUSED, const char *file EINA_UNUSED,
long int dltotal EINA_UNUSED, long int dlnow EINA_UNUSED,
long int ultotal EINA_UNUSED, long int ulnow EINA_UNUSED)
{
Extra_Download_Job *job = data;
double percent = 0.f;
if (dlnow > 0.f)
percent = ((double)(double)dlnow / (double)dltotal);
if (job->progress->progress_cb)
job->progress->progress_cb(percent);
return ECORE_FILE_PROGRESS_CONTINUE;
}
static char *
_extra_theme_preview_remote_generate(Extra_Theme *theme)
{
const char *pattern = "http://" HOSTNAME "/themes/preview/%s.png";
char *url;
EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
url = malloc((strlen(pattern) + strlen(theme->id) - 1) * sizeof(char));
sprintf(url, pattern, theme->id);
return url;
}
static char *
_extra_theme_preview_local_generate(Extra_Theme *theme)
{
Eina_Strbuf *local_preview;
char *tmp;
local_preview = eina_strbuf_new();
eina_strbuf_append(local_preview, efreet_cache_home_get());
eina_strbuf_append_printf(local_preview, "/%s/%s-%d.png", PACKAGE_NAME, theme->id, theme->version);
tmp = eina_strbuf_string_steal(local_preview);
eina_strbuf_free(local_preview);
return tmp;
}
EAPI char*
extra_theme_preview_get(Extra_Theme *theme)
{
char *local;
Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme);
//download is in progress do not return the path
if (priv->state & PREVIEW_DOWNLOAD) return NULL;
local = _extra_theme_preview_local_generate(theme);
if (!ecore_file_exists(local))
{
free(local);
local = NULL;
}
return local;
}
EAPI void
extra_theme_preview_download(Extra_Progress *progress, Extra_Theme *theme)
{
char *remote, *dst;
Extra_Download_Job *job;
Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme);
if (priv->state & PREVIEW_DOWNLOAD) return;
job = calloc(1, sizeof(Extra_Download_Job));
job->progress = progress;
job->theme = priv;
job->nand_mask = PREVIEW_DOWNLOAD;
priv->state |= PREVIEW_DOWNLOAD;
remote = _extra_theme_preview_remote_generate(theme);
dst = _extra_theme_preview_local_generate(theme);
ecore_file_download(remote, dst, _download_complete_cb, _download_progress_cb, job, NULL);
free(remote);
free(dst);
}
EAPI char *
extra_theme_download_url_get(Extra_Theme *theme)
{
const char *pattern = "http://" HOSTNAME "/themes/%s-%d.edj";
char *url;
EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
url = malloc((strlen(pattern) + strlen(theme->id) - 1 + (int)(log10(theme->version))) * sizeof(char));
sprintf(url, pattern, theme->id, theme->version);
return url;
}
EAPI void
extra_theme_download(Extra_Progress *progress, Extra_Theme *theme)
{
char *path, *urlstr = NULL;
Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme);
if (theme)
{
if (priv->state & THEME_DOWNLOAD) return;
Extra_Download_Job *job = calloc(1, sizeof(Extra_Download_Job));
job->progress = progress;
job->theme = priv;
job->nand_mask = THEME_DOWNLOAD;
priv->state |= THEME_DOWNLOAD;
urlstr = extra_theme_download_url_get(theme);
path = extra_theme_install_path_get(theme);
ecore_file_download(urlstr, path, _download_complete_cb, _download_progress_cb, job, NULL);
free(urlstr);
free(path);
}
}
void
_extra_theme_cache_load()
{
char *cache_path = _theme_cache_path_get();
if (ecore_file_exists(cache_path))
{
Eina_File *cache;
Eina_File_Line *line;
Eina_Iterator *it;
Eina_Strbuf *buf;
INF("Loading themes from cache");
cache = eina_file_open(cache_path, EINA_FALSE);
it = eina_file_map_lines(cache);
buf = eina_strbuf_new();
EINA_ITERATOR_FOREACH(it, line)
{
eina_strbuf_append_length(buf, line->start, line->length);
}
_fill_themes(buf);
eina_strbuf_free(buf);
eina_file_close(cache);
}
else
INF("No theme cache found");
free(cache_path);
}
static Eina_Bool
_enlightenment_restart(void *data EINA_UNUSED)
{
char *cmd = "enlightenment_remote -restart";
ecore_exe_run(cmd, NULL);
return EINA_FALSE;
}
EAPI void
extra_theme_use(Extra_Theme *t)
{
char *path;
EINA_SAFETY_ON_NULL_RETURN(t);
path = extra_theme_install_path_get(t);
elm_theme_set(NULL, path);
elm_config_all_flush();
elm_config_save();
free(path);
ecore_timer_add(3, _enlightenment_restart, NULL);
}