Fri, 23 Feb 2018 04:30:06 +0000
Merged in qarkai/pidgin/xdg-dirs (pull request #230)
Migrate to XDG dirs
Approved-by: Eion Robb <eionrobb@gmail.com>
Approved-by: Gary Kramlich <grim@reaperworld.com>
| libpurple/buddyicon.c | file | annotate | diff | comparison | revisions | |
| libpurple/log.c | file | annotate | diff | comparison | revisions | |
| libpurple/util.c | file | annotate | diff | comparison | revisions | |
| libpurple/util.h | file | annotate | diff | comparison | revisions | |
| libpurple/xmlnode.c | file | annotate | diff | comparison | revisions |
--- a/libpurple/buddyicon.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/buddyicon.c Fri Feb 23 04:30:06 2018 +0000 @@ -1095,8 +1095,8 @@ g_free, NULL); pointer_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal); - if (!cache_dir) - cache_dir = g_build_filename(purple_user_dir(), "icons", NULL); + if (!cache_dir) + cache_dir = g_build_filename(purple_cache_dir(), "icons", NULL); } void
--- a/libpurple/core.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/core.c Fri Feb 23 04:30:06 2018 +0000 @@ -263,7 +263,7 @@ purple_prefs_uninit(); purple_plugins_uninit(); - static_proto_unload(); + static_proto_unload(); purple_protocols_uninit(); #ifdef HAVE_DBUS @@ -422,3 +422,30 @@ return ops->get_ui_info(); } + +#define MIGRATE_TO_XDG_DIR(xdg_base_dir, legacy_path) \ + G_STMT_START { \ + gboolean migrate_res; \ + \ + migrate_res = purple_move_to_xdg_base_dir(xdg_base_dir, legacy_path); \ + if (!migrate_res) { \ + purple_debug_error("core", "Error migrating %s to %s\n", \ + legacy_path, xdg_base_dir); \ + return FALSE; \ + } \ + } G_STMT_END + +gboolean +purple_core_migrate_to_xdg_base_dirs(void) +{ + gboolean xdg_dir_exists; + + xdg_dir_exists = g_file_test(purple_data_dir(), G_FILE_TEST_EXISTS); + if (!xdg_dir_exists) { + MIGRATE_TO_XDG_DIR(purple_data_dir(), "certificates"); + MIGRATE_TO_XDG_DIR(purple_data_dir(), "logs"); + MIGRATE_TO_XDG_DIR(purple_config_dir(), "pounces.xml"); + } + + return TRUE; +}
--- a/libpurple/core.h Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/core.h Fri Feb 23 04:30:06 2018 +0000 @@ -226,6 +226,19 @@ */ GHashTable* purple_core_get_ui_info(void); +/** + * purple_core_migrate_to_xdg_base_dirs: + * + * Migrates from legacy directory for libpurple to location following + * XDG base dir spec. https://developer.pidgin.im/ticket/10029 + * NOTE This is not finished yet. Need to decide where other profile files + * should be moved. Search for usages of purple_user_dir(). + * + * Returns: TRUE if migrated successfully, FALSE otherwise. On failure, + * the application must display an error to the user and then exit. + */ +gboolean purple_core_migrate_to_xdg_base_dirs(void); + G_END_DECLS #endif /* _PURPLE_CORE_H_ */
--- a/libpurple/log.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/log.c Fri Feb 23 04:30:06 2018 +0000 @@ -332,7 +332,7 @@ target = purple_escape_filename(purple_normalize(account, name)); } - dir = g_build_filename(purple_user_dir(), "logs", protocol_name, acct_name, target, NULL); + dir = g_build_filename(purple_data_dir(), "logs", protocol_name, acct_name, target, NULL); g_free(acct_name); @@ -1010,7 +1010,7 @@ * functions because they use the same directory structure. */ static void log_get_log_sets_common(GHashTable *sets) { - gchar *log_path = g_build_filename(purple_user_dir(), "logs", NULL); + gchar *log_path = g_build_filename(purple_data_dir(), "logs", NULL); GDir *log_dir = g_dir_open(log_path, 0, NULL); const gchar *protocol; @@ -1526,7 +1526,7 @@ static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account) { char *logfile = g_strdup_printf("%s.log", purple_normalize(account, sn)); - char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL); + char *pathstr = g_build_filename(purple_data_dir(), "logs", logfile, NULL); PurpleStringref *pathref = purple_stringref_new(pathstr); GStatBuf st; time_t log_last_modified; @@ -1788,7 +1788,7 @@ static int old_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account) { char *logfile = g_strdup_printf("%s.log", purple_normalize(account, name)); - char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL); + char *pathstr = g_build_filename(purple_data_dir(), "logs", logfile, NULL); int size; GStatBuf st; @@ -1840,7 +1840,7 @@ static void old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets) { - char *log_path = g_build_filename(purple_user_dir(), "logs", NULL); + char *log_path = g_build_filename(purple_data_dir(), "logs", NULL); GDir *log_dir = g_dir_open(log_path, 0, NULL); gchar *name; PurpleBlistNode *gnode, *cnode, *bnode;
--- a/libpurple/pounce.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/pounce.c Fri Feb 23 04:30:06 2018 +0000 @@ -278,7 +278,7 @@ node = pounces_to_xmlnode(); data = purple_xmlnode_to_formatted_str(node, NULL); - purple_util_write_data_to_file("pounces.xml", data, -1); + purple_util_write_data_to_config_file("pounces.xml", data, -1); g_free(data); purple_xmlnode_free(node); } @@ -534,7 +534,7 @@ g_free(data->account_name); data->ui_name = NULL; - data->pounce = NULL; + data->pouncee = NULL; data->protocol_id = NULL; data->event_type = NULL; data->option_type = NULL; @@ -570,7 +570,7 @@ static gboolean purple_pounces_load(void) { - gchar *filename = g_build_filename(purple_user_dir(), "pounces.xml", NULL); + gchar *filename = g_build_filename(purple_config_dir(), "pounces.xml", NULL); gchar *contents = NULL; gsize length; GMarkupParseContext *context;
--- a/libpurple/protocols/jabber/caps.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/protocols/jabber/caps.c Fri Feb 23 04:30:06 2018 +0000 @@ -206,10 +206,11 @@ char *str; int length = 0; PurpleXmlNode *root = purple_xmlnode_new("capabilities"); + g_hash_table_foreach(capstable, jabber_caps_store_client, root); str = purple_xmlnode_to_formatted_str(root, &length); purple_xmlnode_free(root); - purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, length); + purple_util_write_data_to_cache_file(JABBER_CAPS_FILENAME, str, length); g_free(str); save_timer = 0; @@ -226,7 +227,7 @@ static void jabber_caps_load(void) { - PurpleXmlNode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); + PurpleXmlNode *capsdata = purple_util_read_xml_from_cache_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); PurpleXmlNode *client; if(!capsdata)
--- a/libpurple/theme-manager.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/theme-manager.c Fri Feb 23 04:30:06 2018 +0000 @@ -147,7 +147,7 @@ purple_theme_manager_refresh(void) { gchar *path; - const gchar *xdg; + const gchar *const *xdg_dirs; gint i; GSList *loaders = NULL; @@ -159,27 +159,19 @@ purple_theme_manager_build_dir(loaders, path); g_free(path); - /* look for XDG_DATA_HOME. If we don't have it use ~/.local, and add it */ - if ((xdg = g_getenv("XDG_DATA_HOME")) != NULL) - path = g_build_filename(xdg, "themes", NULL); - else - path = g_build_filename(purple_home_dir(), ".local", "themes", NULL); - + /* look for XDG_DATA_HOME */ + /* NOTE: will work on Windows, see g_get_user_data_dir() documentation */ + path = g_build_filename(g_get_user_data_dir(), "themes", NULL); purple_theme_manager_build_dir(loaders, path); g_free(path); /* now dig through XDG_DATA_DIRS and add those too */ - xdg = g_getenv("XDG_DATA_DIRS"); - if (xdg) { - gchar **xdg_dirs = g_strsplit(xdg, G_SEARCHPATH_SEPARATOR_S, 0); - - for (i = 0; xdg_dirs[i]; i++) { - path = g_build_filename(xdg_dirs[i], "themes", NULL); - purple_theme_manager_build_dir(loaders, path); - g_free(path); - } - - g_strfreev(xdg_dirs); + /* NOTE: will work on Windows, see g_get_system_data_dirs() documentation */ + xdg_dirs = g_get_system_data_dirs(); + for (i = 0; xdg_dirs[i] != NULL; i++) { + path = g_build_filename(xdg_dirs[i], "themes", NULL); + purple_theme_manager_build_dir(loaders, path); + g_free(path); } g_slist_free(loaders);
--- a/libpurple/tls-certificate.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/tls-certificate.c Fri Feb 23 04:30:06 2018 +0000 @@ -32,7 +32,7 @@ static gchar * make_certificate_path(const gchar *id) { - return g_build_filename(purple_user_dir(), + return g_build_filename(purple_data_dir(), "certificates", "tls", id != NULL ? purple_escape_filename(id) : NULL, NULL);
--- a/libpurple/util.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/util.c Fri Feb 23 04:30:06 2018 +0000 @@ -40,6 +40,9 @@ static char *custom_user_dir = NULL; static char *user_dir = NULL; +static gchar *cache_dir = NULL; +static gchar *config_dir = NULL; +static gchar *data_dir = NULL; static JsonNode *escape_js_node = NULL; static JsonGenerator *escape_js_gen = NULL; @@ -156,6 +159,15 @@ g_free(user_dir); user_dir = NULL; + g_free(cache_dir); + cache_dir = NULL; + + g_free(config_dir); + config_dir = NULL; + + g_free(data_dir); + data_dir = NULL; + json_node_free(escape_js_node); escape_js_node = NULL; @@ -3026,6 +3038,83 @@ return user_dir; } +static const gchar * +purple_xdg_dir(gchar **xdg_dir, const gchar *xdg_base_dir, const gchar *xdg_type) +{ + if (!*xdg_dir) { + if (!custom_user_dir) { + *xdg_dir = g_build_filename(xdg_base_dir, "purple", NULL); + } else { + *xdg_dir = g_build_filename(custom_user_dir, xdg_type, NULL); + } + } + + return *xdg_dir; +} + +const gchar * +purple_cache_dir(void) +{ + return purple_xdg_dir(&cache_dir, g_get_user_cache_dir(), "cache"); +} + +const gchar * +purple_config_dir(void) +{ + return purple_xdg_dir(&config_dir, g_get_user_config_dir(), "config"); +} + +const gchar * +purple_data_dir(void) +{ + return purple_xdg_dir(&data_dir, g_get_user_data_dir(), "data"); +} + +gboolean +purple_move_to_xdg_base_dir(const char *purple_xdg_dir, char *path) +{ + gint mkdir_res; + gchar *xdg_path; + gboolean xdg_path_exists; + + /* Create destination directory */ + mkdir_res = purple_build_dir(purple_xdg_dir, S_IRWXU); + if (mkdir_res == -1) { + purple_debug_error("util", "Error creating xdg directory %s: %s; failed migration\n", + purple_xdg_dir, g_strerror(errno)); + return FALSE; + } + + xdg_path = g_build_filename(purple_xdg_dir, path, NULL); + xdg_path_exists = g_file_test(xdg_path, G_FILE_TEST_EXISTS); + if (!xdg_path_exists) { + gchar *old_path; + gboolean old_path_exists; + + old_path = g_build_filename(purple_user_dir(), path, NULL); + old_path_exists = g_file_test(old_path, G_FILE_TEST_EXISTS); + if (old_path_exists) { + int rename_res; + + rename_res = g_rename(old_path, xdg_path); + if (rename_res == -1) { + purple_debug_error("util", "Error renaming %s to %s; failed migration\n", + old_path, xdg_path); + g_free(old_path); + g_free(xdg_path); + + return FALSE; + } + } + + g_free(old_path); + } + + g_free(xdg_path); + + return TRUE; +} + void purple_util_set_user_dir(const char *dir) { g_free(custom_user_dir); @@ -3036,48 +3125,83 @@ custom_user_dir = NULL; } -int purple_build_dir (const char *path, int mode) +int purple_build_dir(const char *path, int mode) { return g_mkdir_with_parents(path, mode); } +static gboolean +purple_util_write_data_to_file_common(const char *dir, const char *filename, const char *data, gssize size) +{ + gchar *filename_full; + gboolean ret = FALSE; + + g_return_val_if_fail(dir != NULL, FALSE); + + purple_debug_misc("util", "Writing file %s to directory %s", + filename, dir); + + /* Ensure the directory exists */ + if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) + { + if (g_mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + purple_debug_error("util", "Error creating directory %s: %s\n", + dir, g_strerror(errno)); + return FALSE; + } + } + + filename_full = g_build_filename(dir, filename, NULL); + + ret = purple_util_write_data_to_file_absolute(filename_full, data, size); + + g_free(filename_full); + return ret; +} + +gboolean +purple_util_write_data_to_file(const char *filename, const char *data, gssize size) +{ + const char *user_dir = purple_user_dir(); + gboolean ret = purple_util_write_data_to_file_common(user_dir, filename, data, size); + + return ret; +} + +gboolean +purple_util_write_data_to_cache_file(const char *filename, const char *data, gssize size) +{ + const char *cache_dir = purple_cache_dir(); + gboolean ret = purple_util_write_data_to_file_common(cache_dir, filename, data, size); + + return ret; +} + +gboolean +purple_util_write_data_to_config_file(const char *filename, const char *data, gssize size) +{ + const char *config_dir = purple_config_dir(); + gboolean ret = purple_util_write_data_to_file_common(config_dir, filename, data, size); + + return ret; +} + +gboolean +purple_util_write_data_to_data_file(const char *filename, const char *data, gssize size) +{ + const char *data_dir = purple_data_dir(); + gboolean ret = purple_util_write_data_to_file_common(data_dir, filename, data, size); + + return ret; +} + /* * This function is long and beautiful, like my--um, yeah. Anyway, * it includes lots of error checking so as we don't overwrite * people's settings if there is a problem writing the new values. */ gboolean -purple_util_write_data_to_file(const char *filename, const char *data, gssize size) -{ - const char *user_dir = purple_user_dir(); - gchar *filename_full; - gboolean ret = FALSE; - - g_return_val_if_fail(user_dir != NULL, FALSE); - - purple_debug_misc("util", "Writing file %s to directory %s", - filename, user_dir); - - /* Ensure the user directory exists */ - if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR)) - { - if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - purple_debug_error("util", "Error creating directory %s: %s\n", - user_dir, g_strerror(errno)); - return FALSE; - } - } - - filename_full = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", user_dir, filename); - - ret = purple_util_write_data_to_file_absolute(filename_full, data, size); - - g_free(filename_full); - return ret; -} - -gboolean purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, gssize size) { gchar *filename_temp; @@ -3234,6 +3358,24 @@ return purple_xmlnode_from_file(purple_user_dir(), filename, description, "util"); } +PurpleXmlNode * +purple_util_read_xml_from_cache_file(const char *filename, const char *description) +{ + return purple_xmlnode_from_file(purple_cache_dir(), filename, description, "util"); +} + +PurpleXmlNode * +purple_util_read_xml_from_config_file(const char *filename, const char *description) +{ + return purple_xmlnode_from_file(purple_config_dir(), filename, description, "util"); +} + +PurpleXmlNode * +purple_util_read_xml_from_data_file(const char *filename, const char *description) +{ + return purple_xmlnode_from_file(purple_data_dir(), filename, description, "util"); +} + /* * Like mkstemp() but returns a file pointer, uses a pre-set template, * uses the semantics of tempnam() for the directory to use and allocates
--- a/libpurple/util.h Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/util.h Fri Feb 23 04:30:06 2018 +0000 @@ -754,15 +754,68 @@ * purple_user_dir: * * Returns the purple settings directory in the user's home directory. - * This is usually ~/.purple + * This is usually $HOME/.purple * * See purple_home_dir() * * Returns: The purple settings directory. + * + * Deprecated: Use purple_cache_dir(), purple_config_dir() or + * purple_data_dir() instead. */ const char *purple_user_dir(void); /** + * purple_cache_dir: + * + * Returns the purple cache directory according to XDG Base Directory Specification. + * This is usually $HOME/.cache/purple. + * If custom user dir was specified then this is cache + * sub-directory of DIR argument passed to -c option. + * + * Returns: The purple cache directory. + */ +const gchar *purple_cache_dir(void); + +/** + * purple_config_dir: + * + * Returns the purple configuration directory according to XDG Base Directory Specification. + * This is usually $HOME/.config/purple. + * If custom user dir was specified then this is config + * sub-directory of DIR argument passed to -c option. + * + * Returns: The purple configuration directory. + */ +const gchar *purple_config_dir(void); + +/** + * purple_data_dir: + * + * Returns the purple data directory according to XDG Base Directory Specification. + * This is usually $HOME/.local/share/purple. + * If custom user dir was specified then this is data + * sub-directory of DIR argument passed to -c option. + * + * Returns: The purple data directory. + */ +const gchar *purple_data_dir(void); + +/** + * purple_move_to_xdg_base_dir: + * @purple_xdg_dir: The path to cache, config or data dir. + * Use respective function + * @path: File or directory in purple_user_dir + * + * Moves file or directory from legacy user dir to XDG + * based dir. + * + * Returns: TRUE if moved successfully, FALSE otherwise + */ +gboolean +purple_move_to_xdg_base_dir(const char *purple_xdg_dir, char *path); + +/** * purple_util_set_user_dir: * @dir: The custom settings directory * @@ -786,7 +839,7 @@ /** * purple_util_write_data_to_file: * @filename: The basename of the file to write in the purple_user_dir. - * @data: A null-terminated string of data to write. + * @data: A string of data to write. * @size: The size of the data to save. If data is * null-terminated you can pass in -1. * @@ -798,14 +851,69 @@ * should work fine for saving binary files as well. * * Returns: TRUE if the file was written successfully. FALSE otherwise. + * + * Deprecated: Use purple_util_write_data_to_cache_file(), + * purple_util_write_data_to_config_file() or + * purple_util_write_data_to_data_file() instead. */ gboolean purple_util_write_data_to_file(const char *filename, const char *data, gssize size); /** + * purple_util_write_data_to_cache_file: + * @filename: The basename of the file to write in the purple_cache_dir. + * @data: A string of data to write. + * @size: The size of the data to save. If data is + * null-terminated you can pass in -1. + * + * Write a string of data to a file of the given name in the Purple + * cache directory ($HOME/.cache/purple by default). + * + * See purple_util_write_data_to_file() + * + * Returns: TRUE if the file was written successfully. FALSE otherwise. + */ +gboolean +purple_util_write_data_to_cache_file(const char *filename, const char *data, gssize size); + +/** + * purple_util_write_data_to_config_file: + * @filename: The basename of the file to write in the purple_config_dir. + * @data: A string of data to write. + * @size: The size of the data to save. If data is + * null-terminated you can pass in -1. + * + * Write a string of data to a file of the given name in the Purple + * config directory ($HOME/.config/purple by default). + * + * See purple_util_write_data_to_file() + * + * Returns: TRUE if the file was written successfully. FALSE otherwise. + */ +gboolean +purple_util_write_data_to_config_file(const char *filename, const char *data, gssize size); + +/** + * purple_util_write_data_to_data_file: + * @filename: The basename of the file to write in the purple_data_dir. + * @data: A string of data to write. + * @size: The size of the data to save. If data is + * null-terminated you can pass in -1. + * + * Write a string of data to a file of the given name in the Purple + * data directory ($HOME/.local/share/purple by default). + * + * See purple_util_write_data_to_file() + * + * Returns: TRUE if the file was written successfully. FALSE otherwise. + */ +gboolean +purple_util_write_data_to_data_file(const char *filename, const char *data, gssize size); + +/** * purple_util_write_data_to_file_absolute: * @filename_full: Filename to write to - * @data: A null-terminated string of data to write. + * @data: A string of data to write. * @size: The size of the data to save. If data is * null-terminated you can pass in -1. * @@ -836,11 +944,69 @@ * * Returns: An PurpleXmlNode tree of the contents of the given file. Or NULL, if * the file does not exist or there was an error reading the file. + * + * Deprecated: Use purple_util_read_xml_from_cache_file(), + * purple_util_read_xml_from_config_file() or + * purple_util_read_xml_from_data_file() instead. */ PurpleXmlNode *purple_util_read_xml_from_file(const char *filename, const char *description); /** + * purple_util_read_xml_from_cache_file: + * @filename: The basename of the file to open in the purple_cache_dir. + * @description: A very short description of the contents of this + * file. This is used in error messages shown to the + * user when the file can not be opened. For example, + * "preferences," or "buddy pounces." + * + * Read the contents of a given file and parse the results into an + * PurpleXmlNode tree structure. This is intended to be used to read + * Purple's cache xml files (xmpp-caps.xml, etc.) + * + * Returns: An PurpleXmlNode tree of the contents of the given file. Or NULL, if + * the file does not exist or there was an error reading the file. + */ +PurpleXmlNode * +purple_util_read_xml_from_cache_file(const char *filename, const char *description); + +/** + * purple_util_read_xml_from_config_file: + * @filename: The basename of the file to open in the purple_config_dir. + * @description: A very short description of the contents of this + * file. This is used in error messages shown to the + * user when the file can not be opened. For example, + * "preferences," or "buddy pounces." + * + * Read the contents of a given file and parse the results into an + * PurpleXmlNode tree structure. This is intended to be used to read + * Purple's config xml files (prefs.xml, pounces.xml, etc.) + * + * Returns: An PurpleXmlNode tree of the contents of the given file. Or NULL, if + * the file does not exist or there was an error reading the file. + */ +PurpleXmlNode * +purple_util_read_xml_from_config_file(const char *filename, const char *description); + +/** + * purple_util_read_xml_from_data_file: + * @filename: The basename of the file to open in the purple_data_dir. + * @description: A very short description of the contents of this + * file. This is used in error messages shown to the + * user when the file can not be opened. For example, + * "preferences," or "buddy pounces." + * + * Read the contents of a given file and parse the results into an + * PurpleXmlNode tree structure. This is intended to be used to read + * Purple's cache xml files (accounts.xml, etc.) + * + * Returns: An PurpleXmlNode tree of the contents of the given file. Or NULL, if + * the file does not exist or there was an error reading the file. + */ +PurpleXmlNode * +purple_util_read_xml_from_data_file(const char *filename, const char *description); + +/** * purple_mkstemp: * @path: The returned path to the temp file. * @binary: Text or binary, for platforms where it matters.
--- a/libpurple/xmlnode.c Fri Feb 16 00:52:52 2018 -0600 +++ b/libpurple/xmlnode.c Fri Feb 23 04:30:06 2018 +0000 @@ -808,7 +808,7 @@ } PurpleXmlNode * -purple_xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process) +purple_xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process) { gchar *filename_full; GError *error = NULL;
--- a/pidgin/gtklog.c Fri Feb 16 00:52:52 2018 -0600 +++ b/pidgin/gtklog.c Fri Feb 23 04:30:06 2018 +0000 @@ -196,7 +196,7 @@ if (log == NULL) - logdir = g_build_filename(purple_user_dir(), "logs", NULL); + logdir = g_build_filename(purple_data_dir(), "logs", NULL); else logdir = purple_log_get_log_dir(log->type, log->name, log->account);