#include "MEGAShellExt.h" #include "mega_ext_client.h" #include G_MODULE_EXPORT void thunar_extension_initialize(ThunarxProviderPlugin *plugin); G_MODULE_EXPORT void thunar_extension_shutdown(void); G_MODULE_EXPORT void thunar_extension_list_types(const GType **types, gint *n_types); static void mega_ext_menu_provider_init(ThunarxMenuProviderIface *iface); static void mega_ext_finalize(GObject *object); static GList* mega_ext_get_file_actions(ThunarxMenuProvider *provider, GtkWidget *window, GList *files); static GList* mega_ext_get_folder_actions(ThunarxMenuProvider *provider, GtkWidget *window, ThunarxFileInfo *folder); static gboolean mega_ext_path_in_sync(MEGAExt *mega_ext, const gchar *path); static GType type_list[1]; #ifdef USING_THUNAR3 #define THUNARITEM ThunarxMenuItem #else #define THUNARITEM GtkAction #endif void thunar_extension_initialize(ThunarxProviderPlugin *plugin) { const gchar *mismatch; mismatch = thunarx_check_version(THUNARX_MAJOR_VERSION, THUNARX_MINOR_VERSION, THUNARX_MICRO_VERSION); if (G_UNLIKELY(mismatch != NULL)) { g_warning("Version mismatch: %s", mismatch); return; } g_message("Initializing MEGAsync extension"); mega_ext_register_type(plugin); type_list[0] = MEGA_TYPE_EXT; } void thunar_extension_shutdown(void) { g_message("Shutting down MEGAsync extension"); } void thunar_extension_list_types(const GType **types, gint *n_types) { *types = type_list; *n_types = G_N_ELEMENTS(type_list); } THUNARX_DEFINE_TYPE_WITH_CODE(MEGAExt, mega_ext, G_TYPE_OBJECT, THUNARX_IMPLEMENT_INTERFACE(THUNARX_TYPE_MENU_PROVIDER, mega_ext_menu_provider_init) ); static void mega_ext_class_init(MEGAExtClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = mega_ext_finalize; } static void mega_ext_finalize(GObject *object) { (*G_OBJECT_CLASS (mega_ext_parent_class)->finalize)(object); } static void mega_ext_init(MEGAExt *mega_ext) { mega_ext->srv_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); } static void mega_ext_menu_provider_init(ThunarxMenuProviderIface *iface) { #ifdef USING_THUNAR3 iface->get_file_menu_items = mega_ext_get_file_actions; iface->get_folder_menu_items = mega_ext_get_folder_actions; #else iface->get_file_actions = mega_ext_get_file_actions; iface->get_folder_actions = mega_ext_get_folder_actions; #endif } 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"; } } // user clicked on "Upload to MEGA" menu item static void mega_ext_on_upload_selected(THUNARITEM *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) { ThunarxFileInfo *file = THUNARX_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = thunarx_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 expanselocalpath(char *path, char *absolutepath) { if (strlen(path) && path[0] == '/') { strcpy(absolutepath, path); char canonical[PATH_MAX]; if (realpath(absolutepath,canonical) != NULL) { strcpy(absolutepath, canonical); } return; } } // user clicked on "Get MEGA link" menu item static void mega_ext_on_get_link_selected(THUNARITEM *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) { ThunarxFileInfo *file = THUNARX_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = thunarx_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(THUNARITEM *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) { ThunarxFileInfo *file = THUNARX_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = thunarx_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(THUNARITEM *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) { ThunarxFileInfo *file = THUNARX_FILE_INFO(l->data); FileState state; gchar *path; GFile *fp; fp = thunarx_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); } static GList* mega_ext_get_file_actions(ThunarxMenuProvider *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) { ThunarxFileInfo *file = THUNARX_FILE_INFO(l->data); gchar *path; GFile *fp; FileState state; fp = thunarx_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 (thunarx_file_info_is_directory(file)) { syncedFolders++; } else { syncedFiles++; } } else { if (thunarx_file_info_is_directory(file)) { unsyncedFolders++; } else { unsyncedFiles++; } } } // if there any unsynced files / folders selected if (unsyncedFiles || unsyncedFolders) { THUNARITEM *item = NULL; out = mega_ext_client_get_string(mega_ext, STRING_UPLOAD, unsyncedFiles, unsyncedFolders); if(out) { g_free(mega_ext->string_upload); mega_ext->string_upload = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::upload_to_mega", mega_ext->string_upload, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION, "name", "MEGAExtension::upload_to_mega", "icon-name", "mega", "label", mega_ext->string_upload, NULL ); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_upload_selected), provider); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(files), (GDestroyNotify)thunarx_file_info_list_free); l_out = g_list_append(l_out, item); } } // if there any synced files / folders selected if (syncedFiles || syncedFolders) { THUNARITEM *item = NULL; out = mega_ext_client_get_string(mega_ext, STRING_GETLINK, syncedFiles, syncedFolders); if(out) { g_free(mega_ext->string_getlink); mega_ext->string_getlink = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::get_mega_link", mega_ext->string_getlink, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION,"name", "MEGAExtension::get_mega_link","icon-name", "mega","label", mega_ext->string_getlink,NULL); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_get_link_selected), provider); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(files), (GDestroyNotify)thunarx_file_info_list_free); l_out = g_list_append(l_out, 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) { THUNARITEM *item = NULL; g_free(mega_ext->string_viewonmega); mega_ext->string_viewonmega = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::view_on_mega", mega_ext->string_viewonmega, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION,"name", "MEGAExtension::view_on_mega","icon-name", "mega","label", mega_ext->string_viewonmega,NULL); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_view_on_mega_selected), provider); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(files), (GDestroyNotify)thunarx_file_info_list_free); l_out = g_list_append(l_out, item); } } else { out = mega_ext_client_get_string(mega_ext, STRING_VIEW_VERSIONS, 0, 0); if(out) { THUNARITEM *item = NULL; g_free(mega_ext->string_viewprevious); mega_ext->string_viewprevious = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::view_previous_versions", mega_ext->string_viewprevious, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION,"name", "MEGAExtension::view_previous_versions","icon-name", "mega","label", mega_ext->string_viewprevious,NULL); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_open_previous_selected), provider); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(files), (GDestroyNotify)thunarx_file_info_list_free); l_out = g_list_append(l_out, item); } } } } return l_out; } static GList* mega_ext_get_folder_actions(ThunarxMenuProvider *provider, G_GNUC_UNUSED GtkWidget *window, ThunarxFileInfo *folder) { MEGAExt *mega_ext = MEGA_EXT(provider); mega_ext->string_upload = NULL; GList *l_out = NULL; int syncedFolders, unsyncedFolders; gchar *out = NULL; // get list of selected objects gchar *path; GFile *fp; FileState state; syncedFolders = unsyncedFolders = 0; fp = thunarx_file_info_get_location(folder); if (!fp) { return NULL; } path = g_file_get_path(fp); if (!path) { return NULL; } // 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, 0); if (state == FILE_NOTFOUND) { char canonical[PATH_MAX]; expanselocalpath(path,canonical); state = mega_ext_client_get_path_state(mega_ext, canonical, 0); } } g_free(path); if (state == FILE_ERROR) { return NULL; } g_debug("State: %s", file_state_to_str(state)); g_object_set_data_full((GObject*)folder, "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) { syncedFolders++; } else { unsyncedFolders++; } // if there any unsynced files / folders selected if (unsyncedFolders) { THUNARITEM *item = NULL; GList *tmp; out = mega_ext_client_get_string(mega_ext, STRING_UPLOAD, 0, unsyncedFolders); g_free(mega_ext->string_upload); mega_ext->string_upload = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::upload_to_mega", mega_ext->string_upload, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION,"name", "MEGAExtension::upload_to_mega","icon-name", "mega","label", mega_ext->string_upload,NULL); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_upload_selected), provider); tmp = g_list_append(NULL, folder); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(tmp), (GDestroyNotify)thunarx_file_info_list_free); g_list_free(tmp); l_out = g_list_append(l_out, item); } // if there any synced files / folders selected if (syncedFolders) { THUNARITEM *item = NULL; GList *tmp; out = mega_ext_client_get_string(mega_ext, STRING_GETLINK, 0, syncedFolders); g_free(mega_ext->string_getlink); mega_ext->string_getlink = g_strdup(out); g_free(out); #ifdef USING_THUNAR3 item = thunarx_menu_item_new("MEGAExtension::get_mega_link", mega_ext->string_getlink, NULL, "mega"); #else item = g_object_new (GTK_TYPE_ACTION,"name", "MEGAExtension::get_mega_link","icon-name", "mega","label", mega_ext->string_getlink,NULL); #endif g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mega_ext_on_get_link_selected), provider); tmp = g_list_append(NULL, folder); g_object_set_data_full(G_OBJECT(item), "MEGAExtension::files", thunarx_file_info_list_copy(tmp), (GDestroyNotify)thunarx_file_info_list_free); g_list_free(tmp); l_out = g_list_append(l_out, item); } return l_out; } // 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; }