--- 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 @@
pointer_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal);
- cache_dir = g_build_filename(purple_user_dir(), "icons", NULL);
+ cache_dir = g_build_filename(purple_cache_dir(), "icons", NULL); --- 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_protocols_uninit();
@@ -422,3 +422,30 @@
return ops->get_ui_info();
+#define MIGRATE_TO_XDG_DIR(xdg_base_dir, legacy_path) \ + gboolean migrate_res; \ + migrate_res = purple_move_to_xdg_base_dir(xdg_base_dir, legacy_path); \ + purple_debug_error("core", "Error migrating %s to %s\n", \ + legacy_path, xdg_base_dir); \ +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); + 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"); --- 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); #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); @@ -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);
@@ -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);
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); @@ -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);
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); purple_xmlnode_free(node);
@@ -534,7 +534,7 @@
g_free(data->account_name);
data->protocol_id = NULL;
data->option_type = NULL;
@@ -570,7 +570,7 @@
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); 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 @@
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); @@ -226,7 +227,7 @@
- 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"); --- 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)
+ const gchar *const *xdg_dirs; @@ -159,27 +159,19 @@
purple_theme_manager_build_dir(loaders, 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);
- 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);
/* now dig through XDG_DATA_DIRS and add those too */
- xdg = g_getenv("XDG_DATA_DIRS");
- 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);
+ /* 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); --- 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 @@
make_certificate_path(const gchar *id)
- return g_build_filename(purple_user_dir(),
+ return g_build_filename(purple_data_dir(), id != NULL ? purple_escape_filename(id) : 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 @@
json_node_free(escape_js_node);
@@ -3026,6 +3038,83 @@
+purple_xdg_dir(gchar **xdg_dir, const gchar *xdg_base_dir, const gchar *xdg_type) + if (!custom_user_dir) { + *xdg_dir = g_build_filename(xdg_base_dir, "purple", NULL); + *xdg_dir = g_build_filename(custom_user_dir, xdg_type, NULL); + return purple_xdg_dir(&cache_dir, g_get_user_cache_dir(), "cache"); + return purple_xdg_dir(&config_dir, g_get_user_config_dir(), "config"); + return purple_xdg_dir(&data_dir, g_get_user_data_dir(), "data"); +purple_move_to_xdg_base_dir(const char *purple_xdg_dir, char *path) + gboolean xdg_path_exists; + /* Create destination directory */ + mkdir_res = purple_build_dir(purple_xdg_dir, S_IRWXU); + purple_debug_error("util", "Error creating xdg directory %s: %s; failed migration\n", + purple_xdg_dir, g_strerror(errno)); + 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) { + 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); + rename_res = g_rename(old_path, xdg_path); + if (rename_res == -1) { + purple_debug_error("util", "Error renaming %s to %s; failed migration\n", void purple_util_set_user_dir(const char *dir)
@@ -3036,48 +3125,83 @@
-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);
+purple_util_write_data_to_file_common(const char *dir, const char *filename, const char *data, gssize size) + g_return_val_if_fail(dir != NULL, FALSE); + purple_debug_misc("util", "Writing file %s to directory %s", + /* 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)); + filename_full = g_build_filename(dir, filename, NULL); + ret = purple_util_write_data_to_file_absolute(filename_full, data, size); +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); +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); +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); +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); * 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.
-purple_util_write_data_to_file(const char *filename, const char *data, gssize size)
- const char *user_dir = purple_user_dir();
- g_return_val_if_fail(user_dir != NULL, FALSE);
- purple_debug_misc("util", "Writing file %s to directory %s",
- /* 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));
- 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);
purple_util_write_data_to_file_absolute(const char *filename_full, const char *data, gssize size)
@@ -3234,6 +3358,24 @@
return purple_xmlnode_from_file(purple_user_dir(), filename, description, "util");
+purple_util_read_xml_from_cache_file(const char *filename, const char *description) + return purple_xmlnode_from_file(purple_cache_dir(), filename, description, "util"); +purple_util_read_xml_from_config_file(const char *filename, const char *description) + return purple_xmlnode_from_file(purple_config_dir(), filename, description, "util"); +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 @@
* Returns the purple settings directory in the user's home directory.
- * This is usually ~/.purple
+ * This is usually $HOME/.purple * Returns: The purple settings directory.
+ * Deprecated: Use purple_cache_dir(), purple_config_dir() or + * purple_data_dir() instead. const char *purple_user_dir(void);
+ * 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); + * 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); + * 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 + * Returns: TRUE if moved successfully, FALSE otherwise +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,
+ * 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. +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. +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. +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. +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. +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. +purple_util_read_xml_from_data_file(const char *filename, const char *description); * @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 @@
-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) --- 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 @@
- logdir = g_build_filename(purple_user_dir(), "logs", NULL);
+ logdir = g_build_filename(purple_data_dir(), "logs", NULL); logdir = purple_log_get_log_dir(log->type, log->name, log->account);