/* gcompris - gameutil_net.c * * Copyright (C) 2006 Bruno Coudoin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "gc_net.h" #include "gc_core.h" #include #ifdef USE_GNET #include #endif #include /* FIXME: Should not be needed, a bug in gnet header ? */ gboolean gnet_http_get (const gchar *url, gchar **buffer, gsize *length, guint *response); #ifdef USE_GNET static GHashTable *server_content = NULL; static GHashTable *cache_content=NULL; #define SUPPORT_OR_RETURN(rv) {if(!gc_prop_get()->server) return rv;} #else #define SUPPORT_OR_RETURN(rv) { return rv; } #endif #ifdef USE_GNET static void load_md5file(GHashTable *ht, gchar *content) { gchar **lines, **keyval; int i; lines = g_strsplit(content, "\n", 0); if(lines && lines[0]) { for(i=0; lines[i]; i++) { keyval = g_strsplit(lines[i], " ", 2); if(keyval && keyval[0]) { g_hash_table_insert(ht, g_strdup(keyval[1]), g_strdup(keyval[0])); } g_strfreev(keyval); } } g_strfreev(lines); } #endif /** Init the network library, must be called once before using it * */ void gc_net_init() { SUPPORT_OR_RETURN(); #ifdef USE_GNET GcomprisProperties *properties = gc_prop_get(); gchar *url; gchar *buf = NULL; gsize buflen; guint response; gnet_init(); /* * Get the content.txt file at the root of the http server and store it in a glist * we then now exactly which files we have there * warning, do not use gc_net_get_url_from_file() since we are in fact buildind the list of file * for it. */ url = g_strdup_printf("%s/%s", properties->server, "/content.txt"); if(gnet_http_get(url, &buf, &buflen, &response) && response == 200) { server_content = g_hash_table_new(g_str_hash, g_str_equal); load_md5file(server_content, buf); } else { /* We did not get the content list, disable network now */ g_free(properties->server); properties->server = NULL; g_warning("Failed to initialize networked GCompris because '%s' is not found", url); } g_free(buf); g_free(url); #endif } void gc_net_destroy(void) { SUPPORT_OR_RETURN(); #ifdef USE_GNET if(server_content) g_hash_table_destroy(server_content); server_content = NULL; #endif } /** return an absolute URL if the given file is part of the file available on our server * * \param file: the file to check * \return: a newly allocated URL or NULL */ gchar * gc_net_get_url_from_file(const gchar *format, ...) { SUPPORT_OR_RETURN(NULL); #ifdef USE_GNET GcomprisProperties *properties = gc_prop_get(); gchar *file, *cache=NULL, *value; va_list args; gboolean cache_ok=FALSE; va_start (args, format); file = g_strdup_vprintf (format, args); va_end (args); g_warning("gc_net_get_url_from_file '%s'", file); value = g_hash_table_lookup(server_content, (gpointer) file); if(value) { cache = g_strconcat(properties->cache_dir, "/", file, NULL); if(g_file_test(cache, G_FILE_TEST_IS_REGULAR)) { gchar * content; gsize length; GMD5 *md5cache, *md5serv; /* calc md5 of cache file */ g_file_get_contents(cache, &content, &length, NULL); md5cache = gnet_md5_new(content, length); g_free(content); md5serv = gnet_md5_new_string(value); cache_ok = gnet_md5_equal(md5serv, md5cache); gnet_md5_delete(md5serv); gnet_md5_delete(md5cache); } if(cache_ok==0) { gchar *url; gchar *buf = NULL; gsize buflen; guint response; url = g_strconcat(properties->server, "/", file, NULL); if(gnet_http_get(url, &buf, &buflen, &response) && response == 200) { gchar *dirname; dirname = g_path_get_dirname(cache); g_mkdir_with_parents(dirname, 0755); g_free(dirname); g_file_set_contents(cache, buf, buflen, NULL); g_free(buf); } else { /* file is in content.txt but not in server */ g_free(cache); cache = NULL; } } } g_free(file); return cache; #endif } #if 0 /** return a glist with the content of the files in the given directory * * \param dir: the directory to scan * \param ext: optional extention filter. e.g. ".xml" to get only *.xml files. * * \return: a new allocated glist that points to internal elements. Do not free the list * data itself. */ GSList *gc_net_dir_read_name(const gchar* dir, const gchar *ext) { SUPPORT_OR_RETURN(NULL); #ifdef USE_GNET GSList *filelist = NULL; GSList *i = NULL; g_return_val_if_fail(dir!=NULL, NULL); for (i = server_content_list; i != NULL; i = g_slist_next (i)) { if(strncmp(dir, (gchar *)i->data, strlen(dir)) == 0) if(ext == NULL || g_str_has_suffix ((gchar *)i->data, ext)) filelist = g_slist_prepend(filelist, i->data); } return(filelist); #endif } #endif #define CONTENT_FILENAME "content.txt" void gc_cache_init(void) { #ifdef USE_GNET gchar *filename; gchar *buf; gsize buflen; cache_content = g_hash_table_new(g_str_hash, g_str_equal); filename = gc_file_find_absolute_writeable(CONTENT_FILENAME); if(g_file_get_contents(filename, &buf, &buflen,NULL)) { load_md5file(cache_content, buf); g_free(buf); } g_free(filename); #endif } #ifdef USE_GNET static gchar *gc_cache_get_relative(gchar *filename) { gchar *filename_content, *dirname; filename_content = gc_file_find_absolute_writeable(CONTENT_FILENAME); dirname = g_path_get_dirname(filename_content); if(g_str_has_prefix(filename, dirname)) filename = filename + strlen(dirname) + 1; g_free(filename_content); g_free(dirname); return filename; } #endif void gc_cache_add(gchar *filename) { #ifdef USE_GNET if(cache_content==NULL) return; if(g_str_has_suffix(filename, CONTENT_FILENAME)) return; filename = gc_cache_get_relative(filename); g_hash_table_insert(cache_content, g_strdup(filename), g_strdup("0")); #endif } gchar* gc_cache_import_pixmap(gchar *filename, gchar *boarddir, gint width, gint height) { #ifdef USE_GNET GdkPixbuf *pixmap; gchar *basename, *file, *ext, *name, *abs; if(!g_path_is_absolute(filename)) return g_strdup(filename); basename = g_path_get_basename(filename); name = g_build_filename(boarddir, basename,NULL); abs = gc_file_find_absolute(name); if(abs && strcmp(abs,filename)==0) { g_free(basename); g_free(abs); return name; } pixmap = gdk_pixbuf_new_from_file_at_size(filename, width, height,NULL); if(!pixmap) { g_free(abs); g_free(basename); g_free(name); return NULL; } file = gc_file_find_absolute_writeable(name); ext = strchr(basename, '.')+1; if(strcmp(ext, "jpg")==0) ext ="jpeg"; gdk_pixbuf_save(pixmap, file, ext, NULL,NULL); g_free(abs); g_free(basename); g_free(file); return name; #else return NULL; #endif } void gc_cache_remove(gchar *filename) { #ifdef USE_GNET g_remove(filename); filename = gc_cache_get_relative(filename); g_hash_table_remove(cache_content, filename); #endif } struct _table_data { FILE *pf; gchar *path; }; #ifdef USE_GNET static void _table_foreach(gpointer key, gpointer value, gpointer user_data) { struct _table_data *data = (struct _table_data*)user_data; gchar * content, *filename; gsize length; GMD5 *md5; if(strcmp(value, "0")==0) { filename = g_build_filename(data->path, (gchar*)key, NULL); if(g_file_get_contents(filename, &content, &length, NULL)) { md5 = gnet_md5_new(content, length); value = gnet_md5_get_string(md5); gnet_md5_delete(md5); g_free(content); } g_free(filename); } if(strcmp(value, "0")) { fprintf(data->pf, "%s %s\n", (gchar*)value, (gchar*)key); } } #endif void gc_cache_save(void) { #ifdef USE_GNET struct _table_data data; FILE *pf; gchar *filename; filename = gc_file_find_absolute_writeable(CONTENT_FILENAME); pf = fopen(filename, "w"); if(!pf) { g_warning("Couldn't save %s\n", filename); return; } data.pf = pf; data.path = g_path_get_dirname(filename); g_hash_table_foreach(cache_content, _table_foreach, &data); g_free(filename); g_free(data.path); fclose(pf); #endif } void gc_cache_destroy(void) { #ifdef USE_GNET gc_cache_save(); g_hash_table_destroy(cache_content); cache_content = NULL; #endif }