Back to index

indicator-appmenu  12.10.0
load-app-info.c
Go to the documentation of this file.
00001 /*
00002 Loads application info for initial app usage and verification
00003 
00004 Copyright 2011 Canonical Ltd.
00005 
00006 Authors:
00007     Ted Gould <ted@canonical.com>
00008 
00009 This program is free software: you can redistribute it and/or modify it 
00010 under the terms of the GNU General Public License version 3, as published 
00011 by the Free Software Foundation.
00012 
00013 This program is distributed in the hope that it will be useful, but 
00014 WITHOUT ANY WARRANTY; without even the implied warranties of 
00015 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00016 PURPOSE.  See the GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License along 
00019 with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 */
00021 
00022 #include <gio/gio.h>
00023 #include <glib/gi18n.h>
00024 
00025 #include "load-app-info.h"
00026 #include "shared-values.h"
00027 
00028 static void new_element (GMarkupParseContext *context, const gchar * name, const gchar ** attribute_names, const gchar ** attribute_values, gpointer user_data, GError **error);
00029 static void end_element (GMarkupParseContext  *context, const gchar * name, gpointer user_data, GError ** error);
00030 
00031 static GMarkupParser app_info_parser = {
00032        start_element:  new_element,
00033        end_element:    end_element,
00034        text:           NULL,
00035        passthrough:    NULL,
00036        error:          NULL
00037 };
00038 
00039 typedef struct _menu_data_t menu_data_t;
00040 struct _menu_data_t {
00041        sqlite3 * db;
00042        gchar * desktopfile;
00043        gchar * domain;
00044        gboolean seen_header;
00045        gboolean seen_menus;
00046        GQueue queue;
00047        GString * statement;
00048 };
00049 
00050 typedef enum _menu_errors_t menu_errors_t;
00051 enum _menu_errors_t {
00052        DUPLICATE_HEADERS,
00053        DUPLICATE_DESKTOPFILE,
00054        DUPLICATE_MENUS,
00055        MISSING_HEADER,
00056        MISSING_DESKTOP,
00057        MISSING_MENUS,
00058        ERROR_LAST
00059 };
00060 
00061 gboolean
00062 load_app_info (const gchar * filename, sqlite3 * db)
00063 {
00064        /* verify */
00065        g_return_val_if_fail(filename != NULL, FALSE);
00066        g_return_val_if_fail(db != NULL, FALSE);
00067 
00068        if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
00069               return FALSE;
00070        }
00071 
00072        /* get data */
00073        GFile * file = g_file_new_for_path(filename);
00074        gchar * data = NULL;
00075        gsize len = 0;
00076        GError * error = NULL;
00077 
00078        gboolean load = g_file_load_contents(file,
00079                                             NULL, /* cancelable */
00080                                             &data,
00081                                             &len,
00082                                             NULL, /* end tag */
00083                                             &error);
00084 
00085        if (error != NULL) {
00086               g_warning("Unable to load file '%s': %s", filename, error->message);
00087               g_error_free(error);
00088               g_object_unref(file);
00089               return FALSE;
00090        }
00091 
00092        if (!load) {
00093               g_warning("Unable to load file '%s'", filename);
00094               g_object_unref(file);
00095               return FALSE;
00096        }
00097 
00098        /* parse it */
00099        menu_data_t menu_data = {
00100               db: db,
00101               seen_header: FALSE,
00102               seen_menus: FALSE,
00103               desktopfile: NULL,
00104               domain: NULL,
00105               queue: G_QUEUE_INIT,
00106               statement: NULL
00107        };
00108 
00109        menu_data.statement = g_string_new("begin transaction;");
00110 
00111        GMarkupParseContext * context = g_markup_parse_context_new(&app_info_parser,
00112                                                                   0, /* flags */
00113                                                                   &menu_data,
00114                                                                   NULL /* destroy func */);
00115 
00116        gboolean parsed = g_markup_parse_context_parse(context, data, len, &error);
00117 
00118        if (error != NULL) {
00119               g_warning("Unable to parse file '%s': %s", filename, error->message);
00120               g_error_free(error);
00121               error = NULL;
00122        }
00123 
00124        if (!parsed) {
00125               g_warning("Unable to parse file '%s'", filename);
00126        }
00127 
00128        if (!menu_data.seen_header) {
00129               g_warning("Never found a header in '%s'", filename);
00130               parsed = FALSE;
00131        }
00132 
00133        if (!menu_data.seen_menus) {
00134               g_warning("Never found menus in '%s'", filename);
00135               parsed = FALSE;
00136        }
00137 
00138        g_markup_parse_context_free(context);
00139 
00140        /* Execute SQL Statement */
00141        /* If we have one */
00142        if (parsed && menu_data.statement->str[0] != '\0') {
00143               g_string_append_printf(menu_data.statement, "end transaction;");
00144 
00145               int exec_status = SQLITE_OK;
00146               gchar * failstring = NULL;
00147               exec_status = sqlite3_exec(db,
00148                                          menu_data.statement->str,
00149                                          NULL, NULL, &failstring);
00150               if (exec_status != SQLITE_OK) {
00151                      g_warning("Unable to execute SQL statement to load DB: %s", failstring);
00152               }
00153        }
00154 
00155        g_free(menu_data.desktopfile);
00156        g_free(menu_data.domain);
00157        g_string_free(menu_data.statement, TRUE);
00158 
00159        /* Free data */
00160        g_free(data);
00161        g_object_unref(file);
00162 
00163        return parsed;
00164 }
00165 
00166 static GQuark
00167 error_domain (void)
00168 {
00169        static GQuark domain = 0;
00170        if (domain == 0) {
00171               domain = g_quark_from_static_string("hud-app-info-parser");
00172        }
00173        return domain;
00174 }
00175 
00176 #define COLLECT(first, ...) \
00177   g_markup_collect_attributes (name,                                         \
00178                                attribute_names, attribute_values, error,     \
00179                                first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
00180 #define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
00181 #define STRDUP     G_MARKUP_COLLECT_STRDUP
00182 #define STRING     G_MARKUP_COLLECT_STRING
00183 #define NO_ATTRS() COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
00184 
00185 static void
00186 new_element (GMarkupParseContext *context, const gchar * name, const gchar ** attribute_names, const gchar ** attribute_values, gpointer user_data, GError **error)
00187 {
00188        menu_data_t * menu_data = (menu_data_t *)user_data;
00189 
00190        if (g_strcmp0(name, "hudappinfo") == 0) {
00191               if (menu_data->seen_header) {
00192                      g_set_error(error, error_domain(), DUPLICATE_HEADERS, "Recieved second header");
00193               }
00194 
00195               menu_data->seen_header = TRUE;
00196               return;
00197        }
00198 
00199        if (!menu_data->seen_header) {
00200               g_set_error(error, error_domain(), MISSING_HEADER, "Missing the header when we got to element '%s'", name);
00201               return;
00202        }
00203 
00204        if (g_strcmp0(name, "desktopfile") == 0) {
00205               const gchar * desktopfile;
00206 
00207               if (!COLLECT(STRING, "path", &desktopfile)) {
00208                      return;
00209               }
00210 
00211               if (menu_data->desktopfile != NULL) {
00212                      g_set_error(error, error_domain(), DUPLICATE_DESKTOPFILE, "Two desktop file definitions.  First as '%s' then as '%s'.", menu_data->desktopfile, desktopfile);
00213                      return;
00214               }
00215 
00216               menu_data->desktopfile = g_strdup(desktopfile);
00217               return;
00218        }
00219 
00220        if (g_strcmp0(name, "menus") == 0) {
00221               if (menu_data->desktopfile == NULL) {
00222                      g_set_error(error, error_domain(), MISSING_DESKTOP, "No desktop file is defined");
00223               }
00224 
00225               if (menu_data->seen_menus) {
00226                      g_set_error(error, error_domain(), DUPLICATE_MENUS, "Second set of menus found");
00227               }
00228 
00229               menu_data->seen_menus = TRUE;
00230 
00231               return;
00232        }
00233 
00234        if (g_strcmp0(name, "menu") == 0) {
00235               if (!menu_data->seen_menus) {
00236                      g_set_error(error, error_domain(), MISSING_MENUS, "Menu tag found without enclosing menus");
00237                      return;
00238               }
00239 
00240               const gchar * mname;
00241 
00242               if (!COLLECT(STRING, "name", &mname)) {
00243                      return;
00244               }
00245 
00246               const gchar * translated = NULL;
00247               if (menu_data->domain != NULL) {
00248                      translated = g_dgettext(menu_data->domain, mname);
00249               } else {
00250                      translated = _(mname);
00251               }
00252 
00253               if (g_queue_is_empty(&menu_data->queue)) {
00254                      g_queue_push_head(&menu_data->queue, g_strdup(translated));
00255               } else {
00256                      g_queue_push_head(&menu_data->queue, g_strconcat((gchar *)g_queue_peek_head(&menu_data->queue), DB_SEPARATOR, translated, NULL));
00257               }
00258 
00259               return;
00260        }
00261 
00262        if (g_strcmp0(name, "item") == 0) {
00263               if (!menu_data->seen_menus) {
00264                      g_set_error(error, error_domain(), MISSING_MENUS, "Item tag found without enclosing menus");
00265                      return;
00266               }
00267 
00268               const gchar * iname;
00269               const gchar * scount;
00270 
00271               if (!COLLECT(STRING, "name", &iname,
00272                            STRING, "count", &scount)) {
00273                      return;
00274               }
00275 
00276               const gchar * translated = NULL;
00277               if (menu_data->domain != NULL) {
00278                      translated = g_dgettext(menu_data->domain, iname);
00279               } else {
00280                      translated = _(iname);
00281               }
00282 
00283               gchar * finalitem = g_strconcat((gchar *)g_queue_peek_head(&menu_data->queue), DB_SEPARATOR, translated, NULL);
00284               gint64 count = g_ascii_strtoll(scount, NULL, 10);
00285 
00286               int i;
00287               for (i = 0; i < count; i++) {
00288                      if (i == 0) {
00289                             g_string_append_printf(menu_data->statement, "insert into usage (application, entry, timestamp) values ('%s', '%s', date('now', 'utc'));", menu_data->desktopfile, finalitem);
00290                      } else {
00291                             g_string_append_printf(menu_data->statement, "insert into usage (application, entry, timestamp) values ('%s', '%s', date('now', 'utc', '-%d days'));", menu_data->desktopfile, finalitem, i);
00292                      }
00293               }
00294 
00295               g_free(finalitem);
00296               return;
00297        }
00298 
00299        return;
00300 }
00301 
00302 static void
00303 end_element (GMarkupParseContext  *context, const gchar * name, gpointer user_data, GError ** error)
00304 {
00305        menu_data_t * menu_data = (menu_data_t *)user_data;
00306 
00307        if (g_strcmp0(name, "menu") == 0) {
00308               if (g_queue_is_empty(&menu_data->queue)) {
00309                      g_warning("Menu stack is empty!");
00310               } else {
00311                      g_free(g_queue_pop_head(&menu_data->queue));
00312               }
00313 
00314               return;
00315        }
00316 
00317        return;
00318 }
00319