comparison src/status.c @ 10418:bed2c96bc1fb

[gaim-migrate @ 11669] I split the status-saving code into it's own little API, because it really is separate from the other status.c savedstatuses.c sits on top of the rest of the status API. And you can delete saved statuses now. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sun, 26 Dec 2004 00:46:26 +0000
parents 823ad21cd95a
children c9b1f3fac753
comparison
equal deleted inserted replaced
10417:823ad21cd95a 10418:bed2c96bc1fb
28 #include "core.h" 28 #include "core.h"
29 #include "debug.h" 29 #include "debug.h"
30 #include "notify.h" 30 #include "notify.h"
31 #include "prefs.h" 31 #include "prefs.h"
32 #include "status.h" 32 #include "status.h"
33 #include "util.h"
34 #include "xmlnode.h"
35 33
36 /** 34 /**
37 * A type of status. 35 * A type of status.
38 */ 36 */
39 struct _GaimStatusType 37 struct _GaimStatusType
120 typedef struct 118 typedef struct
121 { 119 {
122 GaimAccount *account; 120 GaimAccount *account;
123 char *name; 121 char *name;
124 } GaimStatusBuddyKey; 122 } GaimStatusBuddyKey;
125
126 /**
127 * The information of a snap-shot of the statuses of all
128 * your accounts. Basically these are your saved away messages.
129 * There is an overall status and message that applies to
130 * all your accounts, and then each individual account can
131 * optionally have a different custom status and message.
132 *
133 * The changes to status.xml caused by the new status API
134 * are fully backward compatible. The new status API just
135 * adds the optional sub-statuses to the XML file.
136 */
137 struct _GaimStatusSaved
138 {
139 char *title;
140 GaimStatusPrimitive type;
141 char *message;
142
143 GList *substatuses; /**< A list of GaimStatusSavedSub's. */
144 };
145
146 struct _GaimStatusSavedSub
147 {
148 GaimAccount *account;
149 const GaimStatusType *type;
150 char *message;
151 };
152 123
153 static int primitive_scores[] = 124 static int primitive_scores[] =
154 { 125 {
155 0, /* unset */ 126 0, /* unset */
156 -500, /* offline */ 127 -500, /* offline */
163 -10, /* idle, special case. */ 134 -10, /* idle, special case. */
164 -5 /* idle time, special case. */ 135 -5 /* idle time, special case. */
165 }; 136 };
166 137
167 static GHashTable *buddy_presences = NULL; 138 static GHashTable *buddy_presences = NULL;
168 static GList *saved_statuses = NULL;
169 gboolean have_read_saved_statuses = FALSE;
170 139
171 #define SCORE_IDLE 5 140 #define SCORE_IDLE 5
172 #define SCORE_IDLE_TIME 6 141 #define SCORE_IDLE_TIME 6
173
174 /**
175 * Elements of this array correspond to the GaimStatusPrimitive
176 * enumeration.
177 */
178 static const char *primitive_names[] =
179 {
180 "unset",
181 "offline",
182 "available",
183 "unavailable",
184 "hidden",
185 "away",
186 "extended_away"
187 };
188
189 static GaimStatusPrimitive
190 gaim_primitive_get_type(const char *name)
191 {
192 int i;
193
194 g_return_val_if_fail(name != NULL, GAIM_STATUS_UNSET);
195
196 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++)
197 {
198 if (!strcmp(name, primitive_names[i]))
199 return i;
200 }
201
202 return GAIM_STATUS_UNSET;
203 }
204 142
205 /************************************************************************** 143 /**************************************************************************
206 * GaimStatusType API 144 * GaimStatusType API
207 **************************************************************************/ 145 **************************************************************************/
208 GaimStatusType * 146 GaimStatusType *
1698 return TRUE; 1636 return TRUE;
1699 else 1637 else
1700 return FALSE; 1638 return FALSE;
1701 } 1639 }
1702 1640
1703 const GList *
1704 gaim_statuses_get_saved(void)
1705 {
1706 return saved_statuses;
1707 }
1708
1709 GaimStatusSaved *
1710 gaim_statuses_find_saved(const char *title)
1711 {
1712 GList *l;
1713 GaimStatusSaved *status;
1714
1715 for (l = saved_statuses; l != NULL; l = g_list_next(l))
1716 {
1717 status = (GaimStatusSaved *)l->data;
1718 if (!strcmp(status->title, title))
1719 return status;
1720 }
1721
1722 return NULL;
1723 }
1724
1725 const char *
1726 gaim_statuses_saved_get_title(const GaimStatusSaved *saved_status)
1727 {
1728 return saved_status->title;
1729 }
1730
1731 GaimStatusPrimitive
1732 gaim_statuses_saved_get_type(const GaimStatusSaved *saved_status)
1733 {
1734 return saved_status->type;
1735 }
1736
1737 const char *
1738 gaim_statuses_saved_get_message(const GaimStatusSaved *saved_status)
1739 {
1740 return saved_status->message;
1741 }
1742
1743 void * 1641 void *
1744 gaim_statuses_get_handle() { 1642 gaim_status_get_handle(void) {
1745 static int handle; 1643 static int handle;
1746 1644
1747 return &handle; 1645 return &handle;
1748 } 1646 }
1749 1647
1750 void 1648 void
1751 gaim_statuses_init(void) 1649 gaim_status_init(void)
1752 { 1650 {
1753 void *handle = gaim_statuses_get_handle; 1651 void *handle = gaim_status_get_handle;
1754 1652
1755 gaim_prefs_add_none("/core/status"); 1653 gaim_prefs_add_none("/core/status");
1756 gaim_prefs_add_none("/core/status/scores"); 1654 gaim_prefs_add_none("/core/status/scores");
1757 1655
1758 gaim_prefs_add_int("/core/status/scores/offline", 1656 gaim_prefs_add_int("/core/status/scores/offline",
1790 buddy_presences = g_hash_table_new(gaim_buddy_presences_hash, 1688 buddy_presences = g_hash_table_new(gaim_buddy_presences_hash,
1791 gaim_buddy_presences_equal); 1689 gaim_buddy_presences_equal);
1792 } 1690 }
1793 1691
1794 void 1692 void
1795 gaim_statuses_uninit(void) 1693 gaim_status_uninit(void)
1796 { 1694 {
1797 if (buddy_presences != NULL) 1695 if (buddy_presences != NULL)
1798 { 1696 {
1799 g_hash_table_destroy(buddy_presences); 1697 g_hash_table_destroy(buddy_presences);
1800 1698
1801 buddy_presences = NULL; 1699 buddy_presences = NULL;
1802 } 1700 }
1803 } 1701 }
1804
1805 static GaimStatusSavedSub *
1806 gaim_statuses_read_parse_substatus(xmlnode *substatus)
1807 {
1808 GaimStatusSavedSub *ret;
1809 xmlnode *node;
1810 char *data = NULL;
1811
1812 ret = g_new0(GaimStatusSavedSub, 1);
1813
1814 /* Read the account */
1815 node = xmlnode_get_child(substatus, "account");
1816 if (node != NULL)
1817 {
1818 char *acct_name;
1819 const char *protocol;
1820 acct_name = xmlnode_get_data(node);
1821 protocol = xmlnode_get_attrib(node, "protocol");
1822 if ((acct_name != NULL) && (protocol != NULL))
1823 ret->account = gaim_accounts_find(acct_name, protocol);
1824 g_free(acct_name);
1825 }
1826
1827 if (ret->account == NULL)
1828 {
1829 g_free(ret);
1830 return NULL;
1831 }
1832
1833 /* Read the state */
1834 node = xmlnode_get_child(substatus, "state");
1835 if (node != NULL)
1836 data = xmlnode_get_data(node);
1837 if (data != NULL) {
1838 ret->type = gaim_status_type_find_with_id(ret->account->status_types,
1839 data);
1840 g_free(data);
1841 data = NULL;
1842 }
1843
1844 /* Read the message */
1845 node = xmlnode_get_child(substatus, "message");
1846 if (node != NULL)
1847 data = xmlnode_get_data(node);
1848 if (data != NULL)
1849 ret->message = data;
1850
1851 return ret;
1852 }
1853
1854 /**
1855 * Parse a saved status and add it to the saved_statuses linked list.
1856 *
1857 * Here's an example of the XML for a saved status:
1858 * <status name="Girls">
1859 * <state>away</state>
1860 * <message>I like the way that they walk
1861 * And it's chill to hear them talk
1862 * And I can always make them smile
1863 * From White Castle to the Nile</message>
1864 * <substatus>
1865 * <account protocol='prpl-oscar'>markdoliner</account>
1866 * <state>available</state>
1867 * <message>The ladies man is here to answer your queries.</message>
1868 * </substatus>
1869 * <substatus>
1870 * <account protocol='prpl-oscar'>giantgraypanda</account>
1871 * <state>away</state>
1872 * <message>A.C. ain't in charge no more.</message>
1873 * </substatus>
1874 * </status>
1875 *
1876 * I know. Moving, huh?
1877 */
1878 static GaimStatusSaved *
1879 gaim_statuses_read_parse_status(xmlnode *status)
1880 {
1881 GaimStatusSaved *ret;
1882 xmlnode *node;
1883 const char *attrib;
1884 char *data = NULL;
1885 int i;
1886
1887 ret = g_new0(GaimStatusSaved, 1);
1888
1889 /* Read the title */
1890 attrib = xmlnode_get_attrib(status, "name");
1891 if (attrib == NULL)
1892 attrib = "No Title";
1893 /* Ensure the title is unique */
1894 ret->title = g_strdup(attrib);
1895 i = 2;
1896 while (gaim_statuses_find_saved(ret->title) != NULL)
1897 {
1898 g_free(ret->title);
1899 ret->title = g_strdup_printf("%s %d", attrib, i);
1900 i++;
1901 }
1902
1903 /* Read the primitive status type */
1904 node = xmlnode_get_child(status, "state");
1905 if (node != NULL)
1906 data = xmlnode_get_data(node);
1907 if (data != NULL) {
1908 ret->type = gaim_primitive_get_type(data);
1909 g_free(data);
1910 data = NULL;
1911 }
1912
1913 /* Read the message */
1914 node = xmlnode_get_child(status, "message");
1915 if (node != NULL)
1916 data = xmlnode_get_data(node);
1917 if (data != NULL)
1918 ret->message = data;
1919
1920 /* Read substatuses */
1921 for (node = xmlnode_get_child(status, "status"); node != NULL;
1922 node = xmlnode_get_next_twin(node))
1923 {
1924 GaimStatusSavedSub *new;
1925 new = gaim_statuses_read_parse_substatus(node);
1926 if (new != NULL)
1927 ret->substatuses = g_list_append(ret->substatuses, new);
1928 }
1929
1930 return ret;
1931 }
1932
1933 /**
1934 * @return TRUE on success, FALSE on failure (if the file can not
1935 * be opened, or if it contains invalid XML).
1936 */
1937 static gboolean
1938 gaim_statuses_read(const char *filename)
1939 {
1940 GError *error;
1941 gchar *contents = NULL;
1942 gsize length;
1943 xmlnode *statuses, *status;
1944
1945 gaim_debug_info("status", "Reading %s\n", filename);
1946
1947 if (!g_file_get_contents(filename, &contents, &length, &error))
1948 {
1949 gaim_debug_error("status", "Error reading statuses: %s\n",
1950 error->message);
1951 g_error_free(error);
1952 return FALSE;
1953 }
1954
1955 statuses = xmlnode_from_str(contents, length);
1956
1957 if (statuses == NULL)
1958 {
1959 FILE *backup;
1960 gchar *name;
1961 gaim_debug_error("status", "Error parsing statuses\n");
1962 name = g_strdup_printf("%s~", filename);
1963 if ((backup = fopen(name, "w")))
1964 {
1965 fwrite(contents, length, 1, backup);
1966 fclose(backup);
1967 chmod(name, S_IRUSR | S_IWUSR);
1968 }
1969 else
1970 {
1971 gaim_debug_error("status", "Unable to write backup %s\n", name);
1972 }
1973 g_free(name);
1974 g_free(contents);
1975 return FALSE;
1976 }
1977
1978 g_free(contents);
1979
1980 for (status = xmlnode_get_child(statuses, "status"); status != NULL;
1981 status = xmlnode_get_next_twin(status))
1982 {
1983 GaimStatusSaved *new;
1984 new = gaim_statuses_read_parse_status(status);
1985 saved_statuses = g_list_append(saved_statuses, new);
1986 }
1987
1988 gaim_debug_info("status", "Finished reading statuses\n");
1989
1990 xmlnode_free(statuses);
1991
1992 return TRUE;
1993 }
1994
1995 void
1996 gaim_statuses_load(void)
1997 {
1998 const char *user_dir = gaim_user_dir();
1999 gchar *filename;
2000 gchar *msg;
2001
2002 g_return_if_fail(user_dir != NULL);
2003
2004 have_read_saved_statuses = TRUE;
2005
2006 filename = g_build_filename(user_dir, "status.xml", NULL);
2007
2008 if (g_file_test(filename, G_FILE_TEST_EXISTS))
2009 {
2010 if (!gaim_statuses_read(filename))
2011 {
2012 msg = g_strdup_printf(_("An error was encountered parsing the "
2013 "file containing your saved statuses (%s). They "
2014 "have not been loaded, and the old file has been "
2015 "renamed to status.xml~."), filename);
2016 gaim_notify_error(NULL, NULL, _("Saved Statuses Error"), msg);
2017 g_free(msg);
2018 }
2019 }
2020
2021 g_free(filename);
2022 }
2023
2024 static xmlnode *
2025 gaim_substatus_get_as_xmlnode(GaimStatusSavedSub *substatus)
2026 {
2027 xmlnode *node, *child;
2028
2029 node = xmlnode_new("substatus");
2030
2031 child = xmlnode_new("account");
2032 xmlnode_set_attrib(node, "protocol",
2033 gaim_account_get_protocol_id(substatus->account));
2034 xmlnode_insert_data(child,
2035 gaim_account_get_username(substatus->account), -1);
2036 xmlnode_insert_child(node, child);
2037
2038 child = xmlnode_new("state");
2039 xmlnode_insert_data(child, substatus->type->id, -1);
2040 xmlnode_insert_child(node, child);
2041
2042 if (substatus->message != NULL)
2043 {
2044 child = xmlnode_new("message");
2045 xmlnode_insert_data(child, substatus->message, -1);
2046 xmlnode_insert_child(node, child);
2047 }
2048
2049 return node;
2050 }
2051
2052 static xmlnode *
2053 gaim_status_get_as_xmlnode(GaimStatusSaved *status)
2054 {
2055 xmlnode *node, *child;
2056 GList *cur;
2057
2058 node = xmlnode_new("status");
2059 xmlnode_set_attrib(node, "name", status->title);
2060
2061 child = xmlnode_new("state");
2062 xmlnode_insert_data(child, primitive_names[status->type], -1);
2063 xmlnode_insert_child(node, child);
2064
2065 child = xmlnode_new("message");
2066 xmlnode_insert_data(child, status->message, -1);
2067 xmlnode_insert_child(node, child);
2068
2069 for (cur = status->substatuses; cur != NULL; cur = cur->next)
2070 {
2071 child = gaim_substatus_get_as_xmlnode(cur->data);
2072 xmlnode_insert_child(node, child);
2073 }
2074
2075 return node;
2076 }
2077
2078 static xmlnode *
2079 gaim_statuses_get_as_xmlnode()
2080 {
2081 xmlnode *node, *child;
2082 GList *cur;
2083
2084 node = xmlnode_new("statuses");
2085 xmlnode_set_attrib(node, "version", "1");
2086
2087 for (cur = saved_statuses; cur != NULL; cur = cur->next)
2088 {
2089 child = gaim_status_get_as_xmlnode(cur->data);
2090 xmlnode_insert_child(node, child);
2091 }
2092
2093 return node;
2094 }
2095
2096 void
2097 gaim_statuses_sync(void)
2098 {
2099 xmlnode *statuses;
2100 char *data;
2101
2102 if (!have_read_saved_statuses) {
2103 gaim_debug_error("status", "Attempted to save statuses before they "
2104 "were read!\n");
2105 return;
2106 }
2107
2108 statuses = gaim_statuses_get_as_xmlnode();
2109 data = xmlnode_to_formatted_str(statuses, NULL);
2110 gaim_util_write_data_to_file("status.xml", data, -1);
2111 g_free(data);
2112 xmlnode_free(statuses);
2113 }