#include #include #include #include #include "MEGAShellExt.h" #include "mega_ext_client.h" #include "mega_notify_client.h" #include static GObjectClass *parent_class; static void mega_ext_class_init(MEGAExtClass *class) { parent_class = g_type_class_peek_parent(class); } static void mega_ext_instance_init(MEGAExt *mega_ext) { mega_ext->srv_sock = -1; mega_ext->notify_sock = -1; mega_ext->chan = NULL; mega_ext->num_retries = 2; mega_ext->h_syncs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); mega_ext->string_getlink = NULL; mega_ext->string_viewonmega = NULL; mega_ext->string_viewprevious = NULL; mega_ext->string_upload = NULL; mega_ext->syncs_received = FALSE; // ignore SIGPIPE as we most likely will write to a closed socket in mega_notify_client_read() signal(SIGPIPE, SIG_IGN); // start notification client mega_notify_client_timer_start(mega_ext); } static const gchar *file_state_to_str(FileState state) { switch(state) { case FILE_SYNCED: return "synced"; case FILE_PENDING: return "pending"; case FILE_SYNCING: return "syncing"; case FILE_NOTFOUND: default: return "notfound"; } } // received path from notify server with the path to item which state was changed void mega_ext_on_item_changed(MEGAExt *mega_ext, const gchar *path) { GFile *f; f = g_file_new_for_path(path); if (!f) { g_debug("No file found for %s!", path); return; } NemoFileInfo *file = nemo_file_info_lookup(f); if (!file) { g_debug("No NemoFileInfo found for %s!", path); return; } g_debug("Item changed: %s", path); nemo_info_provider_update_file_info((NemoInfoProvider*)mega_ext, file, (void*)1, (void*)1); } // user clicked on "Upload to MEGA" menu item static void mega_ext_on_upload_selected(NemoMenuItem *item, gpointer user_data) { MEGAExt *mega_ext = MEGA_EXT(user_data); GList *l; GList *files; gboolean flag = FALSE; files = g_object_get_data(G_OBJECT(item), "MEGAExtension::files"); for (l = files; l != NULL; l = l->next) { NemoFileInfo *file = NEMO_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = nemo_file_info_get_location(file); if (!fp) continue; path = g_file_get_path(fp); if (!path) continue; state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(file), "MEGAExtension::state")); if (state != FILE_SYNCED && state != FILE_PENDING && state != FILE_SYNCING) { if (mega_ext_client_upload(mega_ext, path)) flag = TRUE; } g_free(path); } if (flag) mega_ext_client_end_request(mega_ext); } void mega_ext_on_sync_add(MEGAExt *mega_ext, const gchar *path) { // ignore empty sync if (!strcmp(path, ".")) return; g_debug("New sync path: %s", path); g_hash_table_insert(mega_ext->h_syncs, g_strdup(path), GINT_TO_POINTER(1)); } void mega_ext_on_sync_del(MEGAExt *mega_ext, const gchar *path) { g_debug("Deleted sync path: %s", path); g_hash_table_remove(mega_ext->h_syncs, path); } void expanselocalpath(char *path, char *absolutepath) { if (strlen(path) && path[0] == '/') { //*absolutepath = *path; strcpy(absolutepath, path); char canonical[PATH_MAX]; if (realpath(absolutepath,canonical) != NULL) { strcpy(absolutepath, canonical); } return; } } // path: a full path to filesystem object // return TRUE if path located in one of the sync folders static gboolean mega_ext_path_in_sync(MEGAExt *mega_ext, const gchar *path) { GList *l, *p; gboolean found = FALSE; l = g_hash_table_get_keys(mega_ext->h_syncs); for (p = g_list_first(l); p; p = g_list_next(p)) { const gchar *sync = p->data; // sync must be a prefix of path if (strlen(sync) <= strlen(path)) { if (!strncmp(sync, path, strlen(sync))) { found = TRUE; break; } } char canonical[PATH_MAX]; expanselocalpath(path,canonical); if (strlen(sync) <= strlen(canonical)) { if (!strncmp(sync, canonical, strlen(sync))) { found = TRUE; break; } } } g_list_free(l); return found; } // user clicked on "Get MEGA link" menu item static void mega_ext_on_get_link_selected(NemoMenuItem *item, gpointer user_data) { MEGAExt *mega_ext = MEGA_EXT(user_data); GList *l; GList *files; gboolean flag = FALSE; files = g_object_get_data(G_OBJECT(item), "MEGAExtension::files"); for (l = files; l != NULL; l = l->next) { NemoFileInfo *file = NEMO_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = nemo_file_info_get_location(file); if (!fp) continue; path = g_file_get_path(fp); if (!path) continue; state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(file), "MEGAExtension::state")); if (state == FILE_SYNCED) { if (mega_ext_client_paste_link(mega_ext, path)) flag = TRUE; } g_free(path); } if (flag) mega_ext_client_end_request(mega_ext); } // user clicked on "View on MEGA" menu item static void mega_ext_on_view_on_mega_selected(NemoMenuItem *item, gpointer user_data) { MEGAExt *mega_ext = MEGA_EXT(user_data); GList *l; GList *files; gboolean flag = FALSE; files = g_object_get_data(G_OBJECT(item), "MEGAExtension::files"); for (l = files; l != NULL; l = l->next) { NemoFileInfo *file = NEMO_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = nemo_file_info_get_location(file); if (!fp) continue; path = g_file_get_path(fp); if (!path) continue; state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(file), "MEGAExtension::state")); if (state == FILE_SYNCED) { if (mega_ext_client_open_link(mega_ext, path)) flag = TRUE; } g_free(path); } if (flag) mega_ext_client_end_request(mega_ext); } // user clicked on "View previous versions" menu item static void mega_ext_on_open_previous_selected(NemoMenuItem *item, gpointer user_data) { MEGAExt *mega_ext = MEGA_EXT(user_data); GList *l; GList *files; gboolean flag = FALSE; files = g_object_get_data(G_OBJECT(item), "MEGAExtension::files"); for (l = files; l != NULL; l = l->next) { NemoFileInfo *file = NEMO_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = nemo_file_info_get_location(file); if (!fp) continue; path = g_file_get_path(fp); if (!path) continue; state = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(file), "MEGAExtension::state")); if (state == FILE_SYNCED) { if (mega_ext_client_open_previous(mega_ext, path)) flag = TRUE; } g_free(path); } if (flag) mega_ext_client_end_request(mega_ext); } // Executed on context menu for selected object(-s) // Check the state of the selected files // to show the menu item "Upload to MEGA", "Get MEGA link" or both // Return: list of NemoMenuItem static GList *mega_ext_get_file_items(NemoMenuProvider *provider, G_GNUC_UNUSED GtkWidget *window, GList *files) { MEGAExt *mega_ext = MEGA_EXT(provider); GList *l, *l_out = NULL; int syncedFiles, syncedFolders, unsyncedFiles, unsyncedFolders; gchar *out = NULL; g_debug("mega_ext_get_file_items: %u", g_list_length(files)); syncedFiles = syncedFolders = unsyncedFiles = unsyncedFolders = 0; // get list of selected objects for (l = files; l != NULL; l = l->next) { NemoFileInfo *file = NEMO_FILE_INFO(l->data); gchar *path; GFile *fp; FileState state; fp = nemo_file_info_get_location(file); if (!fp) { continue; } path = g_file_get_path(fp); if (!path) { continue; } // avoid sending requests for files which are not in synced folders // but make sure we received the list of synced folders first if (mega_ext->syncs_received && !mega_ext_path_in_sync(mega_ext, path)) { state = FILE_NOTFOUND; } else { state = mega_ext_client_get_path_state(mega_ext, path, 1); if (state == FILE_NOTFOUND) { char canonical[PATH_MAX]; expanselocalpath(path,canonical); state = mega_ext_client_get_path_state(mega_ext, canonical, 1); } } g_free(path); if (state == FILE_ERROR) { continue; } g_debug("State: %s", file_state_to_str(state)); g_object_set_data_full((GObject*)file, "MEGAExtension::state", GINT_TO_POINTER(state), NULL); // count the number of synced / unsynced files and folders if (state == FILE_SYNCED || state == FILE_SYNCING || state == FILE_PENDING) { if (nemo_file_info_get_file_type(file) == G_FILE_TYPE_DIRECTORY) { syncedFolders++; } else { syncedFiles++; } } else { if (nemo_file_info_get_file_type(file) == G_FILE_TYPE_DIRECTORY) { unsyncedFolders++; } else { unsyncedFiles++; } } } NemoMenuItem *root_menu_item = nemo_menu_item_new("NemoObj::root_menu_item", "MEGA", "Select MEGA action", "mega"); NemoMenu *subMenu = nemo_menu_new(); nemo_menu_item_set_submenu(root_menu_item, subMenu); //Connect submenu to root menu item // if there any unsynced files / folders selected if (unsyncedFiles || unsyncedFolders) { NemoMenuItem *item; out = mega_ext_client_get_string(mega_ext, STRING_UPLOAD, unsyncedFiles, unsyncedFolders); if(out) { item = nemo_menu_item_new("MEGAExtension::upload_to_mega", out, "Upload files to you MEGA account", "mega"); g_free(mega_ext->string_upload); mega_ext->string_upload = g_strdup(out); g_free(out); g_signal_connect(item, "activate", G_CALLBACK(mega_ext_on_upload_selected), provider); g_object_set_data_full((GObject*)item, "MEGAExtension::files", nemo_file_info_list_copy(files), (GDestroyNotify)nemo_file_info_list_free); nemo_menu_append_item(subMenu, item); g_object_unref(item); } } // if there any synced files / folders selected if (syncedFiles || syncedFolders) { NemoMenuItem *item; out = mega_ext_client_get_string(mega_ext, STRING_GETLINK, syncedFiles, syncedFolders); if(out) { item = nemo_menu_item_new("MEGAExtension::get_mega_link", out, "Get MEGA link", "mega"); g_free(mega_ext->string_getlink); mega_ext->string_getlink = g_strdup(out); g_free(out); g_signal_connect(item, "activate", G_CALLBACK(mega_ext_on_get_link_selected), provider); g_object_set_data_full((GObject*)item, "MEGAExtension::files", nemo_file_info_list_copy(files), (GDestroyNotify)nemo_file_info_list_free); nemo_menu_append_item(subMenu, item); g_object_unref(item); } if ( ((syncedFiles + syncedFolders) == 1 ) && ( (unsyncedFiles+unsyncedFolders) == 0 ) ) { if (syncedFolders) { out = mega_ext_client_get_string(mega_ext, STRING_VIEW_ON_MEGA, 0, 0); if(out) { item = nemo_menu_item_new("MEGAExtension::view_on_mega", out, "View on MEGA", "mega"); g_free(mega_ext->string_viewonmega); mega_ext->string_viewonmega = g_strdup(out); g_free(out); g_signal_connect(item, "activate", G_CALLBACK(mega_ext_on_view_on_mega_selected), provider); g_object_set_data_full((GObject*)item, "MEGAExtension::files", nemo_file_info_list_copy(files), (GDestroyNotify)nemo_file_info_list_free); nemo_menu_append_item(subMenu, item); g_object_unref(item); } } else { out = mega_ext_client_get_string(mega_ext, STRING_VIEW_VERSIONS, 0, 0); if(out) { item = nemo_menu_item_new("MEGAExtension::view_previous_versions", out, "View previous versions", "mega"); g_free(mega_ext->string_viewprevious); mega_ext->string_viewprevious = g_strdup(out); g_free(out); g_signal_connect(item, "activate", G_CALLBACK(mega_ext_on_open_previous_selected), provider); g_object_set_data_full((GObject*)item, "MEGAExtension::files", nemo_file_info_list_copy(files), (GDestroyNotify)nemo_file_info_list_free); nemo_menu_append_item(subMenu, item); g_object_unref(item); } } } } GList *submenus = nemo_menu_get_items(subMenu); if (submenus) { if (g_list_length(submenus)) { l_out = g_list_append(l_out, root_menu_item); } nemo_menu_item_list_free(submenus); } g_object_unref(subMenu); return l_out; } static NemoOperationResult mega_ext_update_file_info(NemoInfoProvider *provider, NemoFileInfo *file, G_GNUC_UNUSED GClosure *update_complete, G_GNUC_UNUSED NemoOperationHandle **handle) { MEGAExt *mega_ext = MEGA_EXT(provider); gchar *path; GFile *fp; FileState state; fp = nemo_file_info_get_location(file); if (!fp) { return NEMO_OPERATION_COMPLETE; } path = g_file_get_path(fp); if (!path) { return NEMO_OPERATION_COMPLETE; } // process items located in sync folders if (mega_ext->syncs_received && !mega_ext_path_in_sync(mega_ext, path)) { g_free(path); return NEMO_OPERATION_COMPLETE; } g_debug("mega_ext_update_file_info %s", path); state = mega_ext_client_get_path_state(mega_ext, path, 0); if (state == FILE_NOTFOUND) { char canonical[PATH_MAX]; expanselocalpath(path,canonical); state = mega_ext_client_get_path_state(mega_ext, canonical, 0); } g_debug("mega_ext_update_file_info. File: %s State: %s", path, file_state_to_str(state)); g_free(path); // reset nemo_file_info_invalidate_extension_info(file); if (state == FILE_ERROR || state == FILE_NOTFOUND) { return NEMO_OPERATION_COMPLETE; } switch (state) { case FILE_SYNCED: nemo_file_info_add_emblem(file, "mega-nemosynced"); break; case FILE_PENDING: nemo_file_info_add_emblem(file, "mega-nemopending"); break; case FILE_SYNCING: nemo_file_info_add_emblem(file, "mega-nemosyncing"); break; default: break; } return NEMO_OPERATION_COMPLETE; } static void mega_ext_menu_provider_iface_init(NemoMenuProviderIface *iface) { iface->get_file_items = mega_ext_get_file_items; } static void mega_ext_info_provider_iface_init(NemoInfoProviderIface *iface) { iface->update_file_info = mega_ext_update_file_info; } static GType mega_ext_type = 0; GType mega_ext_get_type (void) { return mega_ext_type; } void mega_ext_register_type(GTypeModule *module) { static const GTypeInfo mega_type_info = { sizeof(MEGAExtClass), NULL, NULL, (GClassInitFunc)mega_ext_class_init, NULL, NULL, sizeof (MEGAExt), 0, (GInstanceInitFunc)mega_ext_instance_init, NULL }; static const GInterfaceInfo menu_provider_iface_info = { (GInterfaceInitFunc) mega_ext_menu_provider_iface_init, NULL, NULL }; static const GInterfaceInfo info_provider_iface_info = { (GInterfaceInitFunc) mega_ext_info_provider_iface_init, NULL, NULL }; mega_ext_type = g_type_module_register_type(module, G_TYPE_OBJECT, "MEGAExtension", &mega_type_info, 0); g_type_module_add_interface(module, mega_ext_type, NEMO_TYPE_MENU_PROVIDER, &menu_provider_iface_info); g_type_module_add_interface(module, mega_ext_type, NEMO_TYPE_INFO_PROVIDER, &info_provider_iface_info); }