Mercurial > pidgin.yaz
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 } |