/* * * Copyright (C) 2005-2010 by Raymond Huang * plushuang at users.sourceforge.net * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * --- * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU Lesser General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * */ #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // UgPluginClass // void ug_plugin_class_register (const UgPluginClass* plugin_class) { const gchar** string; plugin_class->global_init (); ug_class_register (plugin_class->name, UG_CLASS_PLUGIN, (gpointer) plugin_class); if (plugin_class->schemes) { for (string = plugin_class->schemes; *string; string++) ug_class_register (*string, UG_CLASS_PLUGIN_SCHEME, (gpointer) plugin_class); } if (plugin_class->file_types) { for (string = plugin_class->file_types; *string; string++) ug_class_register (*string, UG_CLASS_PLUGIN_FILE_TYPE, (gpointer) plugin_class); } } void ug_plugin_class_unregister (const UgPluginClass* plugin_class) { const gchar** string; ug_class_unregister (plugin_class->name, UG_CLASS_PLUGIN); if (plugin_class->schemes) { for (string = plugin_class->schemes; *string; string++) ug_class_unregister (*string, UG_CLASS_PLUGIN_SCHEME); } if (plugin_class->file_types) { for (string = plugin_class->file_types; *string; string++) ug_class_unregister (*string, UG_CLASS_PLUGIN_FILE_TYPE); } plugin_class->global_finalize (); } const UgPluginClass* ug_plugin_class_find (const gchar* name, const gchar* type) { if (type == NULL) type = UG_CLASS_PLUGIN; return (const UgPluginClass*) ug_class_find (name, type); } // --------------------------------------------------------------------------- // UgPlugin : UgPlugin is a base structure for downloading. // UgPlugin* ug_plugin_new (const UgPluginClass* plugin_class, UgDataset* dataset) { UgPlugin* plugin; UgInitFunc init; plugin = g_malloc0 (plugin_class->instance_size); // initialize base data plugin->plugin_class = plugin_class; plugin->lock = g_mutex_new (); plugin->dataset = ug_data_copy (dataset); // plugin->queue = NULL; // plugin->state = UG_STATE_STOP; plugin->ref_count = 1; init = plugin_class->init; if (init) init (plugin); return plugin; } UgPlugin* ug_plugin_new_by_name (const gchar* name, UgDataset* dataset) { const UgPluginClass* plugin_class; plugin_class = ug_plugin_class_find (name, NULL); if (plugin_class == NULL) return NULL; return ug_plugin_new (plugin_class, dataset); } UgPlugin* ug_plugin_new_by_data (UgDataset* dataset) { const UgPluginClass* plugin_class; UgDataCommon* common; UgUrlPart* urlpart; UgPathPart* pathpart; gchar* string; plugin_class = NULL; common = ug_dataset_get (dataset, UgDataCommonClass, 0); if (common == NULL) return NULL; if (common->url) { urlpart = ug_url_part_new (common->url, -1); if (urlpart->url_scheme_len) { string = g_strndup (urlpart->url, urlpart->url_scheme_len); plugin_class = ug_plugin_class_find (string, UG_CLASS_PLUGIN_SCHEME); g_free (string); } ug_url_part_free (urlpart); } else if (common->file) { pathpart = ug_path_part_new (common->file, -1); if (pathpart->file_ext_len) { string = g_strndup (pathpart->file_ext, pathpart->file_ext_len); plugin_class = ug_plugin_class_find (string, UG_CLASS_PLUGIN_FILE_TYPE); g_free (string); } ug_path_part_free (pathpart); } if (plugin_class == NULL) return NULL; return ug_plugin_new (plugin_class, dataset); } void ug_plugin_ref (UgPlugin* plugin) { plugin->ref_count++; } void ug_plugin_unref (UgPlugin* plugin) { UgFinalizeFunc finalize; plugin->ref_count--; if (plugin->ref_count == 0) { finalize = plugin->plugin_class->finalize; if (finalize) finalize (plugin); // finalize base data g_mutex_free (plugin->lock); ug_data_free (plugin->dataset); ug_data_list_free (plugin->queue); g_free (plugin); } } void ug_plugin_lock (UgPlugin* plugin) { g_mutex_lock (plugin->lock); } void ug_plugin_unlock (UgPlugin* plugin) { g_mutex_unlock (plugin->lock); } void ug_plugin_delay (UgPlugin* plugin, guint millisecond) { gulong u_second = millisecond * 1000; while (plugin->state == UG_STATE_RUNNING) { if (u_second < 250000) { g_usleep (u_second); return; } g_usleep (250000); u_second -= 250000; } } gboolean ug_plugin_dispatch (UgPlugin* plugin, UgCallback callback, gpointer callback_data) { UgMessage* message; UgMessage* prev; UgMessage* tail; g_mutex_lock (plugin->lock); message = plugin->queue; plugin->queue = NULL; g_mutex_unlock (plugin->lock); if (callback) { for (tail = ug_data_list_last (message); tail; tail = tail->prev) { // if progress repeat, skip current message. if (tail->type == UG_MESSAGE_PROGRESS) { prev = tail->prev; if (prev && prev->type == UG_MESSAGE_PROGRESS) continue; } callback (plugin, tail, callback_data); } } ug_data_list_free (message); return (plugin->state == UG_STATE_RUNNING); } void ug_plugin_post (UgPlugin* plugin, UgMessage* message) { g_mutex_lock (plugin->lock); plugin->queue = ug_data_list_prepend (plugin->queue, message); g_mutex_unlock (plugin->lock); } // concatenate folder and file to new path and try create folder and empty file. // If folder create failed, post UG_MESSAGE_ERROR_FOLDER_CREATE_FAILED. // If file exist, it will change filename and post UG_MESSAGE_DATA_FILE_CHANGED. // if function succeeded, return new path and it's folder length. gchar* ug_plugin_create_file (UgPlugin* plugin, const gchar* folder, const gchar* file, guint* folder_len) { GString* gstr; const gchar* tail_str; guint head_len; guint location_len; guint count; int fd; if (folder == NULL && file == NULL) { ug_plugin_post (plugin, ug_message_new_error (UG_MESSAGE_ERROR_FILE_CREATE_FAILED, NULL)); return NULL; } // concatenate full path gstr = g_string_sized_new (80); if (folder) { g_string_append (gstr, folder); if (gstr->len && gstr->str [gstr->len -1] != G_DIR_SEPARATOR) g_string_append_c (gstr, G_DIR_SEPARATOR); } if (file) g_string_append (gstr, file); // get location_len (length of path - filename). tail_str = strrchr (gstr->str, G_DIR_SEPARATOR); if (tail_str == NULL) location_len = 0; else location_len = tail_str - gstr->str + 1; // + G_DIR_SEPARATOR // create folder if (location_len && ug_create_dir_all (gstr->str, location_len) == -1) { ug_plugin_post (plugin, ug_message_new_error (UG_MESSAGE_ERROR_FOLDER_CREATE_FAILED, NULL)); return g_string_free (gstr, TRUE); } // get head_len (length of folder + primary filename) // and tail_str ('.' + filename extension) tail_str = strrchr (gstr->str + location_len, '.'); if (tail_str == NULL) tail_str = gstr->str + gstr->len; head_len = tail_str - gstr->str; tail_str = g_strdup (tail_str); // create file for (count=0; count < 100; count++) { // fd = open (gstr->str, O_CREAT | O_EXCL | O_RDWR, // S_IREAD | S_IWRITE | S_IRGRP | S_IROTH); fd = ug_fd_open (gstr->str, UG_FD_O_CREATE | UG_FD_O_EXCL | UG_FD_O_RDWR, UG_FD_S_IREAD | UG_FD_S_IWRITE | UG_FD_S_IRGRP | UG_FD_S_IROTH); if (fd != -1) { // close (fd); ug_fd_close (fd); break; } g_string_truncate (gstr, head_len); g_string_append_printf (gstr, "(%d)", count); g_string_append (gstr, tail_str); } // free tail_str g_free ((gpointer) tail_str); // result if (count == 100) { ug_plugin_post (plugin, ug_message_new_error (UG_MESSAGE_ERROR_FILE_CREATE_FAILED, NULL)); return g_string_free (gstr, TRUE); } if (count > 0) ug_plugin_post (plugin, ug_message_new_data (UG_MESSAGE_DATA_FILE_CHANGED, gstr->str + location_len)); if (folder_len) *folder_len = location_len; return g_string_free (gstr, FALSE); } // rename file from old_utf8 to new_utf8. // If file rename fail, it will post UG_MESSAGE_WARNING_FILE_RENAME_FAILED and return FALSE. gboolean ug_plugin_rename_file (UgPlugin* plugin, const gchar* old_utf8, const gchar* new_utf8) { if (ug_rename_file (old_utf8, new_utf8) == 0) return TRUE; // file rename failed. ug_plugin_post (plugin, ug_message_new_warning (UG_MESSAGE_WARNING_FILE_RENAME_FAILED, NULL)); return FALSE; } // --- virtual functions --- UgResult ug_plugin_set_state (UgPlugin* plugin, UgState state) { UgSetStateFunc set_state = plugin->plugin_class->set_state; if (set_state) return set_state (plugin, state); return UG_RESULT_UNSUPPORT; } UgResult ug_plugin_get_state (UgPlugin* plugin, UgState* state) { UgGetStateFunc get_state = plugin->plugin_class->get_state; if (get_state) return get_state (plugin, state); return UG_RESULT_UNSUPPORT; } UgResult ug_plugin_get (UgPlugin* plugin, guint parameter, gpointer data) { UgGetFunc get = plugin->plugin_class->get; if (get) return get (plugin, parameter, data); return UG_RESULT_UNSUPPORT; }