File: | build-analysis/../src/clients/lib/xmmsclient/result.c |
Warning: | line 576, column 6 Use of memory after it is freed |
1 | /* XMMS2 - X Music Multiplexer System | |||||
2 | * Copyright (C) 2003-2017 XMMS2 Team | |||||
3 | * | |||||
4 | * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! | |||||
5 | * | |||||
6 | * This library is free software; you can redistribute it and/or | |||||
7 | * modify it under the terms of the GNU Lesser General Public | |||||
8 | * License as published by the Free Software Foundation; either | |||||
9 | * version 2.1 of the License, or (at your option) any later version. | |||||
10 | * | |||||
11 | * This library is distributed in the hope that it will be useful, | |||||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
14 | * Lesser General Public License for more details. | |||||
15 | */ | |||||
16 | ||||||
17 | #include <stdio.h> | |||||
18 | #include <stdlib.h> | |||||
19 | #include <sys/types.h> | |||||
20 | #include <string.h> | |||||
21 | #include <ctype.h> | |||||
22 | ||||||
23 | #include <sys/types.h> | |||||
24 | ||||||
25 | #include <xmmsclient/xmmsclient.h> | |||||
26 | #include <xmmsclientpriv/xmmsclient.h> | |||||
27 | #include <xmmsclientpriv/xmmsclient_ipc.h> | |||||
28 | #include <xmmsc/xmmsc_idnumbers.h> | |||||
29 | #include <xmmsc/xmmsc_errorcodes.h> | |||||
30 | #include <xmmsc/xmmsc_stdint.h> | |||||
31 | #include <xmmsc/xmmsc_stdbool.h> | |||||
32 | #include <xmmscpriv/xmmsv_c2c.h> | |||||
33 | ||||||
34 | typedef enum { | |||||
35 | XMMSC_RESULT_CALLBACK_DEFAULT, | |||||
36 | XMMSC_RESULT_CALLBACK_RAW, | |||||
37 | XMMSC_RESULT_CALLBACK_C2C | |||||
38 | } xmmsc_result_callback_type_t; | |||||
39 | ||||||
40 | typedef struct xmmsc_result_callback_St { | |||||
41 | xmmsc_result_callback_type_t type; | |||||
42 | xmmsc_result_notifier_t func; | |||||
43 | ||||||
44 | void *user_data; | |||||
45 | xmmsc_user_data_free_func_t free_func; | |||||
46 | } xmmsc_result_callback_t; | |||||
47 | ||||||
48 | static void xmmsc_result_restart (xmmsc_result_t *res); | |||||
49 | static void xmmsc_result_notifier_add (xmmsc_result_t *res, xmmsc_result_callback_t *cb); | |||||
50 | static void xmmsc_result_notifier_remove (xmmsc_result_t *res, x_list_t *node); | |||||
51 | static void xmmsc_result_notifier_delete (xmmsc_result_t *res, x_list_t *node); | |||||
52 | static void xmmsc_result_notifier_delete_all (xmmsc_result_t *res); | |||||
53 | ||||||
54 | static xmmsc_result_callback_t *xmmsc_result_callback_new_default (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f); | |||||
55 | static xmmsc_result_callback_t *xmmsc_result_callback_new_raw (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f); | |||||
56 | static xmmsc_result_callback_t *xmmsc_result_callback_new_c2c (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f); | |||||
57 | ||||||
58 | struct xmmsc_result_St { | |||||
59 | xmmsc_connection_t *c; | |||||
60 | ||||||
61 | /** refcounting */ | |||||
62 | int ref; | |||||
63 | ||||||
64 | xmmsc_result_type_t type; | |||||
65 | ||||||
66 | /** notifiers */ | |||||
67 | x_list_t *notifiers; | |||||
68 | ||||||
69 | xmmsc_ipc_t *ipc; | |||||
70 | ||||||
71 | bool_Bool parsed; | |||||
72 | bool_Bool is_c2c; | |||||
73 | ||||||
74 | uint32_t cookie; | |||||
75 | uint32_t restart_signal; | |||||
76 | ||||||
77 | xmmsv_t *data; | |||||
78 | ||||||
79 | xmmsc_visualization_t *visc; | |||||
80 | }; | |||||
81 | ||||||
82 | /** | |||||
83 | * @defgroup Result Result | |||||
84 | * @brief Result manipulation and error handling | |||||
85 | * @ingroup XMMSClient | |||||
86 | * | |||||
87 | * Each command to the server will return a #xmmsc_result_t | |||||
88 | * to the programmer. This object will be used to see the results | |||||
89 | * off the call. It will handle errors and the results. | |||||
90 | * | |||||
91 | * results could be used in both sync and async fashions. Here | |||||
92 | * is a sync example: | |||||
93 | * @code | |||||
94 | * xmmsc_result_t *res; | |||||
95 | * xmmsv_t *val; | |||||
96 | * uint32_t id; | |||||
97 | * res = xmmsc_playback_get_current_id (connection); | |||||
98 | * xmmsc_result_wait (res); | |||||
99 | * if (!val = xmmsc_result_get_value (res)) { | |||||
100 | * printf ("error: failed to retrieve value!"); | |||||
101 | * } | |||||
102 | * if (xmmsv_is_error (val)) { | |||||
103 | * printf ("error: %s", xmmsv_get_error (val)); | |||||
104 | * } | |||||
105 | * xmmsv_get_uint (val, &id); | |||||
106 | * xmmsc_result_unref (res); | |||||
107 | * printf ("current id is: %d", id); | |||||
108 | * @endcode | |||||
109 | * | |||||
110 | * an async example is a bit more complex... | |||||
111 | * @code | |||||
112 | * static void handler (xmmsv_t *val, void *userdata) { | |||||
113 | * uint32_t id; | |||||
114 | * if (xmmsv_is_error (val)) { | |||||
115 | * printf ("error: %s", xmmsv_get_error (val)); | |||||
116 | * } | |||||
117 | * xmmsv_get_uint (val, &id); | |||||
118 | * printf ("current id is: %d", id); | |||||
119 | * } | |||||
120 | * | |||||
121 | * int main () { | |||||
122 | * // Connect blah blah ... | |||||
123 | * xmmsc_result_t *res; | |||||
124 | * res = xmmsc_playback_get_current_id (connection); | |||||
125 | * xmmsc_result_notifier_set (res, handler); | |||||
126 | * xmmsc_result_unref (res); | |||||
127 | * } | |||||
128 | * @endcode | |||||
129 | * When the answer arrives handler will be called. with the resulting #xmmsv_t | |||||
130 | * @{ | |||||
131 | **/ | |||||
132 | ||||||
133 | /** | |||||
134 | * References the #xmmsc_result_t | |||||
135 | * | |||||
136 | * @param result the result to reference. | |||||
137 | * @return result | |||||
138 | */ | |||||
139 | xmmsc_result_t * | |||||
140 | xmmsc_result_ref (xmmsc_result_t *res) | |||||
141 | { | |||||
142 | x_return_val_if_fail (res, NULL)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 142); return (((void*)0)); }; | |||||
143 | res->ref++; | |||||
144 | ||||||
145 | return res; | |||||
146 | } | |||||
147 | ||||||
148 | /** | |||||
149 | * @todo Deallocate all types here | |||||
150 | */ | |||||
151 | static void | |||||
152 | xmmsc_result_free (xmmsc_result_t *res) | |||||
153 | { | |||||
154 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 154); return; }; | |||||
155 | ||||||
156 | /* Free memory! */ | |||||
157 | if (res->ipc) { | |||||
158 | xmmsc_ipc_result_unregister (res->ipc, res); | |||||
159 | } | |||||
160 | ||||||
161 | if (res->data) { | |||||
162 | xmmsv_unref (res->data); | |||||
163 | } | |||||
164 | ||||||
165 | xmmsc_result_notifier_delete_all (res); | |||||
166 | ||||||
167 | free (res); | |||||
168 | } | |||||
169 | ||||||
170 | /** | |||||
171 | * Get the class of the result (default, signal, broadcast). | |||||
172 | * @returns The class of the result of type #xmmsc_result_type_t | |||||
173 | */ | |||||
174 | xmmsc_result_type_t | |||||
175 | xmmsc_result_get_class (xmmsc_result_t *res) | |||||
176 | { | |||||
177 | x_return_val_if_fail (res, XMMSC_RESULT_CLASS_DEFAULT)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 177); return (XMMSC_RESULT_CLASS_DEFAULT); }; | |||||
178 | ||||||
179 | return res->type; | |||||
180 | } | |||||
181 | ||||||
182 | /** | |||||
183 | * Disconnect all notifiers for a signal or a broadcast result. | |||||
184 | * @param res The result to disconnect, must be of class signal or broadcast. | |||||
185 | */ | |||||
186 | void | |||||
187 | xmmsc_result_disconnect (xmmsc_result_t *res) | |||||
188 | { | |||||
189 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 189); return; }; | |||||
190 | ||||||
191 | switch (res->type) { | |||||
192 | case XMMSC_RESULT_CLASS_SIGNAL: | |||||
193 | case XMMSC_RESULT_CLASS_BROADCAST: | |||||
194 | case XMMSC_RESULT_CLASS_DEFAULT: | |||||
195 | xmmsc_result_notifier_delete_all (res); | |||||
196 | break; | |||||
197 | default: | |||||
198 | x_api_error_if (1, "invalid result type",)if (1) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "%s was called %s" , __FUNCTION__, ("invalid result type")); return ; }; | |||||
199 | } | |||||
200 | } | |||||
201 | ||||||
202 | static void | |||||
203 | xmmsc_result_restart (xmmsc_result_t *res) | |||||
204 | { | |||||
205 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 205); return; }; | |||||
206 | x_return_if_fail (res->c)if (!(res->c)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "res->c", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 206); return; }; | |||||
207 | ||||||
208 | if (res->type != XMMSC_RESULT_CLASS_SIGNAL) { | |||||
209 | x_api_warning ("result is not restartable")xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "%s was called %s" , __FUNCTION__, ("result is not restartable")); | |||||
210 | return; | |||||
211 | } | |||||
212 | ||||||
213 | res->cookie = xmmsc_write_signal_msg (res->c, res->restart_signal); | |||||
214 | } | |||||
215 | ||||||
216 | static bool_Bool | |||||
217 | xmmsc_result_parse_msg (xmmsc_result_t *res, xmms_ipc_msg_t *msg) | |||||
218 | { | |||||
219 | if (xmms_ipc_msg_get_cmd (msg) == XMMS_IPC_COMMAND_ERROR) { | |||||
220 | /* If special error msg, extract the error and save in result */ | |||||
221 | const char *errstr; | |||||
222 | xmmsv_t *error; | |||||
223 | ||||||
224 | if (!xmms_ipc_msg_get_value (msg, &error)) { | |||||
225 | xmmsc_result_seterror (res, "No error value!"); | |||||
226 | } else { | |||||
227 | if (!xmmsv_get_error (error, &errstr)) { | |||||
228 | xmmsc_result_seterror (res, "No error message!"); | |||||
229 | } else { | |||||
230 | xmmsc_result_seterror (res, errstr); | |||||
231 | } | |||||
232 | ||||||
233 | xmmsv_unref (error); | |||||
234 | } | |||||
235 | ||||||
236 | res->parsed = true1; | |||||
237 | return true1; | |||||
238 | } else if (xmms_ipc_msg_get_value (msg, &res->data)) { | |||||
239 | /* Expected message data retrieved! */ | |||||
240 | res->parsed = true1; | |||||
241 | return true1; | |||||
242 | } else { | |||||
243 | /* FIXME: shouldn't parsed be false then? */ | |||||
244 | return false0; | |||||
245 | } | |||||
246 | } | |||||
247 | ||||||
248 | ||||||
249 | /** | |||||
250 | * return the cookie of a resultset. | |||||
251 | */ | |||||
252 | uint32_t | |||||
253 | xmmsc_result_cookie_get (xmmsc_result_t *res) | |||||
254 | { | |||||
255 | x_return_val_if_fail (res, 0)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 255); return (0); }; | |||||
256 | ||||||
257 | return res->cookie; | |||||
258 | } | |||||
259 | ||||||
260 | /** | |||||
261 | * Set a result to be a client-to-client result. | |||||
262 | */ | |||||
263 | void | |||||
264 | xmmsc_result_c2c_set (xmmsc_result_t *res) | |||||
265 | { | |||||
266 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 266); return; }; | |||||
267 | ||||||
268 | res->is_c2c = true1; | |||||
269 | } | |||||
270 | ||||||
271 | void | |||||
272 | xmmsc_result_visc_set (xmmsc_result_t *res, xmmsc_visualization_t *visc) | |||||
273 | { | |||||
274 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 274); return; }; | |||||
275 | x_return_if_fail (!res->visc)if (!(!res->visc)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "!res->visc", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 275); return; }; | |||||
276 | res->visc = visc; | |||||
277 | } | |||||
278 | ||||||
279 | xmmsc_visualization_t * | |||||
280 | xmmsc_result_visc_get (xmmsc_result_t *res) | |||||
281 | { | |||||
282 | x_return_val_if_fail (res, NULL)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 282); return (((void*)0)); }; | |||||
283 | x_return_val_if_fail (res->visc, NULL)if (!(res->visc)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "res->visc", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 283); return (((void *)0)); }; | |||||
284 | return res->visc; | |||||
285 | } | |||||
286 | ||||||
287 | xmmsc_connection_t * | |||||
288 | xmmsc_result_get_connection (xmmsc_result_t *res) | |||||
289 | { | |||||
290 | x_return_val_if_fail (res, NULL)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 290); return (((void*)0)); }; | |||||
291 | x_return_val_if_fail (res->c, NULL)if (!(res->c)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "res->c", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 291); return (((void *)0)); }; | |||||
292 | return res->c; | |||||
293 | } | |||||
294 | ||||||
295 | /** | |||||
296 | * Decreases the references for the #xmmsc_result_t | |||||
297 | * When the number of references reaches 0 it will | |||||
298 | * be freed. And thus all data you extracted from it | |||||
299 | * will be deallocated. | |||||
300 | */ | |||||
301 | ||||||
302 | void | |||||
303 | xmmsc_result_unref (xmmsc_result_t *res) | |||||
304 | { | |||||
305 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 305); return; }; | |||||
306 | x_api_error_if (res->ref < 1, "with a freed result",)if (res->ref < 1) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "%s was called %s", __FUNCTION__, ("with a freed result")); return ; }; | |||||
307 | ||||||
308 | res->ref--; | |||||
309 | if (res->ref == 0) { | |||||
310 | xmmsc_result_free (res); | |||||
311 | } | |||||
312 | } | |||||
313 | ||||||
314 | /* Macro magic to define the xmmsc_result_*_notifier_set. */ | |||||
315 | #define GEN_RESULT_NOTIFIER_SET_FUNC(type)void xmmsc_result_notifier_set_type (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data) { xmmsc_result_notifier_set_type_full (res, func, user_data, ((void*)0)); } \ | |||||
316 | void \ | |||||
317 | xmmsc_result_notifier_set_##type (xmmsc_result_t *res, \ | |||||
318 | xmmsc_result_notifier_t func, \ | |||||
319 | void *user_data) \ | |||||
320 | { \ | |||||
321 | xmmsc_result_notifier_set_##type##_full (res, func, user_data, NULL((void*)0)); \ | |||||
322 | } | |||||
323 | ||||||
324 | /* And more macro magic to define the xmmsc_result_*_notifier_set_full. */ | |||||
325 | #define GEN_RESULT_NOTIFIER_SET_FULL_FUNC(type)void xmmsc_result_notifier_set_type_full (xmmsc_result_t *res , xmmsc_result_notifier_t func, void *user_data, xmmsc_user_data_free_func_t free_func) { xmmsc_result_callback_t *cb; if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 325); return; }; if (!(func)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "func", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 325); return; }; cb = xmmsc_result_callback_new_type (func, user_data, free_func); xmmsc_result_notifier_add (res, cb); } \ | |||||
326 | void \ | |||||
327 | xmmsc_result_notifier_set_##type##_full (xmmsc_result_t *res, \ | |||||
328 | xmmsc_result_notifier_t func, \ | |||||
329 | void *user_data, \ | |||||
330 | xmmsc_user_data_free_func_t free_func) \ | |||||
331 | { \ | |||||
332 | xmmsc_result_callback_t *cb; \ | |||||
333 | \ | |||||
334 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 334); return; }; \ | |||||
335 | x_return_if_fail (func)if (!(func)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "func", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 335); return; }; \ | |||||
336 | \ | |||||
337 | cb = xmmsc_result_callback_new_##type (func, user_data, free_func); \ | |||||
338 | xmmsc_result_notifier_add (res, cb); \ | |||||
339 | } | |||||
340 | ||||||
341 | /** | |||||
342 | * Set up a default callback for the result retrieval. This callback | |||||
343 | * will be called when the answer arrives. | |||||
344 | * The callback receives the value as sent by the server or another | |||||
345 | * client, that is, for c2c messages only the payload is passed to the | |||||
346 | * callback. | |||||
347 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
348 | * @param func the function that should be called when we receive the answer | |||||
349 | * @param user_data optional user data to the callback | |||||
350 | */ | |||||
351 | ||||||
352 | GEN_RESULT_NOTIFIER_SET_FUNC (default)void xmmsc_result_notifier_set_default (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data) { xmmsc_result_notifier_set_default_full (res, func, user_data, ((void*)0)); } | |||||
353 | ||||||
354 | /** | |||||
355 | * Set up a default callback for the result retrieval. This callback | |||||
356 | * will be called when the answer arrives. This function differs from | |||||
357 | * xmmsc_result_default_notifier_set in the additional free_func parameter, | |||||
358 | * which allows to pass a pointer to a function which will be called | |||||
359 | * to free the user_data when needed. | |||||
360 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
361 | * @param func the function that should be called when we receive the answer | |||||
362 | * @param user_data optional user data to the callback | |||||
363 | * @param free_func optional function that should be called to free the user_data | |||||
364 | */ | |||||
365 | ||||||
366 | GEN_RESULT_NOTIFIER_SET_FULL_FUNC (default)void xmmsc_result_notifier_set_default_full (xmmsc_result_t * res, xmmsc_result_notifier_t func, void *user_data, xmmsc_user_data_free_func_t free_func) { xmmsc_result_callback_t *cb; if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 366); return; }; if (!(func)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "func", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 366); return; }; cb = xmmsc_result_callback_new_default (func , user_data, free_func); xmmsc_result_notifier_add (res, cb); } | |||||
367 | ||||||
368 | /** | |||||
369 | * Set up a raw callback for the result retrieval. This callback | |||||
370 | * will be called when the answer arrives. | |||||
371 | * The client receives the value sent by the server or, for | |||||
372 | * client-to-client messages, the full message, whose fields can | |||||
373 | * be extracted with the appropriate functions. | |||||
374 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
375 | * @param func the function that should be called when we receive the answer | |||||
376 | * @param user_data optional user data to the callback | |||||
377 | * | |||||
378 | * \sa xmmsv_c2c_message_get_payload and others. | |||||
379 | */ | |||||
380 | ||||||
381 | GEN_RESULT_NOTIFIER_SET_FUNC (raw)void xmmsc_result_notifier_set_raw (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data) { xmmsc_result_notifier_set_raw_full ( res, func, user_data, ((void*)0)); } | |||||
382 | ||||||
383 | /** | |||||
384 | * Set up a raw callback for the result retrieval. This callback | |||||
385 | * will be called when the answer arrives. This function differs from | |||||
386 | * xmmsc_result_raw_notifier_set in the additional free_func parameter, | |||||
387 | * which allows to pass a pointer to a function which will be called | |||||
388 | * to free the user_data when needed. | |||||
389 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
390 | * @param func the function that should be called when we receive the answer | |||||
391 | * @param user_data optional user data to the callback | |||||
392 | * @param free_func optional function that should be called to free the user_data | |||||
393 | */ | |||||
394 | ||||||
395 | GEN_RESULT_NOTIFIER_SET_FULL_FUNC (raw)void xmmsc_result_notifier_set_raw_full (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data, xmmsc_user_data_free_func_t free_func) { xmmsc_result_callback_t *cb; if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 395); return; }; if (!(func)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "func", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 395); return; }; cb = xmmsc_result_callback_new_raw (func, user_data , free_func); xmmsc_result_notifier_add (res, cb); } | |||||
396 | ||||||
397 | /** | |||||
398 | * Set up a c2c callback for the result retrieval. This callback | |||||
399 | * will be called when the answer arrives. | |||||
400 | * This callback always receives values formatted as client-to-client | |||||
401 | * messages, whose fields can be extracted with the appropriate functions. | |||||
402 | * For values sent by the server, the sender id and message id fields | |||||
403 | * will be zero. | |||||
404 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
405 | * @param func the function that should be called when we receive the answer | |||||
406 | * @param user_data optional user data to the callback | |||||
407 | * | |||||
408 | * \sa xmmsv_c2c_message_get_payload and others. | |||||
409 | */ | |||||
410 | ||||||
411 | GEN_RESULT_NOTIFIER_SET_FUNC (c2c)void xmmsc_result_notifier_set_c2c (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data) { xmmsc_result_notifier_set_c2c_full ( res, func, user_data, ((void*)0)); } | |||||
412 | ||||||
413 | /** | |||||
414 | * Set up a c2c callback for the result retrieval. This callback | |||||
415 | * will be called when the answer arrives. This function differs from | |||||
416 | * xmmsc_result_c2c_notifier_set in the additional free_func parameter, | |||||
417 | * which allows to pass a pointer to a function which will be called | |||||
418 | * to free the user_data when needed. | |||||
419 | * @param res a #xmmsc_result_t that you got from a command dispatcher. | |||||
420 | * @param func the function that should be called when we receive the answer | |||||
421 | * @param user_data optional user data to the callback | |||||
422 | * @param free_func optional function that should be called to free the user_data | |||||
423 | */ | |||||
424 | ||||||
425 | GEN_RESULT_NOTIFIER_SET_FULL_FUNC (c2c)void xmmsc_result_notifier_set_c2c_full (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data, xmmsc_user_data_free_func_t free_func) { xmmsc_result_callback_t *cb; if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 425); return; }; if (!(func)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "func", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 425); return; }; cb = xmmsc_result_callback_new_c2c (func, user_data , free_func); xmmsc_result_notifier_add (res, cb); } | |||||
426 | ||||||
427 | /** | |||||
428 | * Block for the reply. In a synchronous application this | |||||
429 | * can be used to wait for the result. Will return when | |||||
430 | * the server replyed. | |||||
431 | */ | |||||
432 | ||||||
433 | void | |||||
434 | xmmsc_result_wait (xmmsc_result_t *res) | |||||
435 | { | |||||
436 | const char *err = NULL((void*)0); | |||||
437 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 437); return; }; | |||||
438 | x_return_if_fail (res->ipc)if (!(res->ipc)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "res->ipc", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 438); return; }; | |||||
439 | ||||||
440 | while (!res->parsed && !(err = xmmsc_ipc_error_get (res->ipc))) { | |||||
441 | xmmsc_ipc_wait_for_event (res->ipc, 5); | |||||
442 | } | |||||
443 | ||||||
444 | if (err) { | |||||
445 | /* FIXME: xmmsv_unref (res->data) or not allocated ? */ | |||||
446 | res->data = xmmsv_new_error (err); | |||||
447 | } | |||||
448 | } | |||||
449 | ||||||
450 | /** | |||||
451 | * @defgroup ResultValueRetrieval ResultValueRetrieval | |||||
452 | * @ingroup Result | |||||
453 | * @brief Explains how you can retrive values from a #xmmsc_result_t | |||||
454 | * @{ | |||||
455 | */ | |||||
456 | ||||||
457 | /** | |||||
458 | * Get the value from a result. The reference is still owned by the | |||||
459 | * result. | |||||
460 | * | |||||
461 | * @param res a #xmmsc_result_t containing the value. | |||||
462 | * @returns A borrowed reference to the value received by the result. | |||||
463 | */ | |||||
464 | xmmsv_t * | |||||
465 | xmmsc_result_get_value (xmmsc_result_t *res) | |||||
466 | { | |||||
467 | x_return_val_if_fail (res, NULL)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 467); return (((void*)0)); }; | |||||
468 | x_return_val_if_fail (res->parsed, NULL)if (!(res->parsed)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL , "Check '%s' failed in %s at %s:%d", "res->parsed", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 468); return (((void *)0)); }; | |||||
469 | ||||||
470 | return res->data; | |||||
471 | } | |||||
472 | ||||||
473 | /** @} */ | |||||
474 | ||||||
475 | /** @} */ | |||||
476 | ||||||
477 | /** @internal */ | |||||
478 | ||||||
479 | /* Kept as a proxy for external use */ | |||||
480 | void | |||||
481 | xmmsc_result_seterror (xmmsc_result_t *res, const char *errstr) | |||||
482 | { | |||||
483 | if (res->data) { | |||||
484 | xmmsv_unref (res->data); | |||||
485 | } | |||||
486 | res->data = xmmsv_new_error (errstr); | |||||
487 | } | |||||
488 | ||||||
489 | void | |||||
490 | xmmsc_result_restartable (xmmsc_result_t *res, uint32_t signalid) | |||||
491 | { | |||||
492 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 492); return; }; | |||||
493 | ||||||
494 | res->restart_signal = signalid; | |||||
495 | } | |||||
496 | ||||||
497 | /** | |||||
498 | * Helper for xmmsc_result_run. | |||||
499 | * Correctly calls a callback based on its type. | |||||
500 | * @param res The result that owns the callback | |||||
501 | * @param cb The callback to be called | |||||
502 | * @return The result of the callback | |||||
503 | */ | |||||
504 | static int | |||||
505 | xmmsc_result_run_callback (xmmsc_result_t *res, xmmsc_result_callback_t *cb) | |||||
506 | { | |||||
507 | int ret; | |||||
508 | xmmsv_t *val = NULL((void*)0); | |||||
509 | ||||||
510 | if (res->is_c2c && !xmmsv_is_error (res->data)) { | |||||
511 | if (cb->type == XMMSC_RESULT_CALLBACK_DEFAULT) { | |||||
512 | /* For default callbacks, pass the payload in c2c | |||||
513 | * messages. | |||||
514 | */ | |||||
515 | val = xmmsv_ref (xmmsv_c2c_message_get_payload (res->data)); | |||||
516 | } | |||||
517 | } else { | |||||
518 | if (cb->type == XMMSC_RESULT_CALLBACK_C2C) { | |||||
519 | /* For c2c callbacks and non-c2c messages, build a | |||||
520 | * message with pseudo c2c metadata. | |||||
521 | */ | |||||
522 | val = xmmsv_c2c_message_format (0, 0, 0, res->data); | |||||
523 | } | |||||
524 | } | |||||
525 | ||||||
526 | if (!val) { | |||||
527 | val = xmmsv_ref (res->data); | |||||
528 | } | |||||
529 | ||||||
530 | ret = cb->func (val, cb->user_data); | |||||
531 | ||||||
532 | xmmsv_unref (val); | |||||
533 | return ret; | |||||
534 | } | |||||
535 | ||||||
536 | /** | |||||
537 | * @internal | |||||
538 | */ | |||||
539 | void | |||||
540 | xmmsc_result_run (xmmsc_result_t *res, xmms_ipc_msg_t *msg) | |||||
541 | { | |||||
542 | x_list_t *n, *next; | |||||
543 | xmmsc_result_callback_t *cb; | |||||
544 | ||||||
545 | x_return_if_fail (res)if (!(res)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "res", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 545); return; }; | |||||
| ||||||
546 | x_return_if_fail (msg)if (!(msg)) { xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Check '%s' failed in %s at %s:%d" , "msg", __FUNCTION__, "../src/clients/lib/xmmsclient/result.c" , 546); return; }; | |||||
547 | ||||||
548 | if (!xmmsc_result_parse_msg (res, msg)) { | |||||
549 | xmms_ipc_msg_destroy (msg); | |||||
550 | return; | |||||
551 | } | |||||
552 | ||||||
553 | xmms_ipc_msg_destroy (msg); | |||||
554 | ||||||
555 | xmmsc_result_ref (res); | |||||
556 | ||||||
557 | /* Run all notifiers and check for positive return values */ | |||||
558 | n = res->notifiers; | |||||
559 | while (n) { | |||||
560 | int keep; | |||||
561 | ||||||
562 | next = x_list_next (n)((n) ? (((x_list_t *)(n))->next) : ((void*)0)); | |||||
563 | cb = n->data; | |||||
564 | ||||||
565 | keep = xmmsc_result_run_callback (res, cb); | |||||
566 | if (!keep || res->type == XMMSC_RESULT_CLASS_DEFAULT) { | |||||
567 | xmmsc_result_notifier_delete (res, n); | |||||
568 | } | |||||
569 | ||||||
570 | n = next; | |||||
571 | } | |||||
572 | ||||||
573 | /* If this result is a signal, and we still have some notifiers | |||||
574 | * we need to restart the signal. | |||||
575 | */ | |||||
576 | if (res->notifiers && res->type == XMMSC_RESULT_CLASS_SIGNAL) { | |||||
| ||||||
577 | /* We restart the signal using the same result. */ | |||||
578 | xmmsc_result_restart (res); | |||||
579 | } | |||||
580 | ||||||
581 | if (res->type != XMMSC_RESULT_CLASS_DEFAULT && !res->is_c2c) { | |||||
582 | /* We keep the results alive with signals and broadcasts, | |||||
583 | but we just renew the value because it went out of scope. | |||||
584 | (freeing the payload, forget about it). | |||||
585 | The exception being for c2c results of the BROADCAST class | |||||
586 | (those associated with messages and replies), which may be | |||||
587 | used synchronously as if they belonged to the DEFAULT class. | |||||
588 | */ | |||||
589 | xmmsv_unref (res->data); | |||||
590 | res->data = NULL((void*)0); | |||||
591 | } | |||||
592 | ||||||
593 | xmmsc_result_unref (res); | |||||
594 | } | |||||
595 | ||||||
596 | /** | |||||
597 | * Allocates new #xmmsc_result_t and references it. | |||||
598 | * Should not be used from a client. | |||||
599 | * @internal | |||||
600 | */ | |||||
601 | ||||||
602 | xmmsc_result_t * | |||||
603 | xmmsc_result_new (xmmsc_connection_t *c, xmmsc_result_type_t type, | |||||
604 | uint32_t cookie) | |||||
605 | { | |||||
606 | xmmsc_result_t *res; | |||||
607 | ||||||
608 | res = x_new0 (xmmsc_result_t, 1)calloc (1, sizeof (xmmsc_result_t) * (1)); | |||||
609 | if (!res) { | |||||
610 | x_oom ()xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d" , __FUNCTION__, "../src/clients/lib/xmmsclient/result.c", 610 ); | |||||
611 | return NULL((void*)0); | |||||
612 | } | |||||
613 | ||||||
614 | res->c = c; | |||||
615 | ||||||
616 | res->data = NULL((void*)0); | |||||
617 | res->type = type; | |||||
618 | res->cookie = cookie; | |||||
619 | ||||||
620 | /* user must give this back */ | |||||
621 | xmmsc_result_ref (res); | |||||
622 | ||||||
623 | /* Add it to the loop */ | |||||
624 | xmmsc_ipc_result_register (c->ipc, res); | |||||
625 | ||||||
626 | /* For the destroy func */ | |||||
627 | res->ipc = c->ipc; | |||||
628 | ||||||
629 | return res; | |||||
630 | } | |||||
631 | ||||||
632 | void | |||||
633 | xmmsc_result_clear_weakrefs (xmmsc_result_t *result) | |||||
634 | { | |||||
635 | result->c = NULL((void*)0); | |||||
636 | result->ipc = NULL((void*)0); | |||||
637 | } | |||||
638 | ||||||
639 | /* Macro-magically define the xmmsc_result_callback_new_* functions. */ | |||||
640 | #define GEN_RESULT_CALLBACK_NEW_FUNC(name, cbtype)static xmmsc_result_callback_t * xmmsc_result_callback_new_name (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f) { xmmsc_result_callback_t *cb; cb = calloc (1, sizeof (xmmsc_result_callback_t) * (1)); if (!cb) { xmmsc_log ("xmmsclient" , XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 640); return ((void *)0); } cb->type = cbtype; cb->user_data = udata; cb-> free_func = free_f; cb->func = f; return cb; } \ | |||||
641 | static xmmsc_result_callback_t * \ | |||||
642 | xmmsc_result_callback_new_##name (xmmsc_result_notifier_t f, void *udata, \ | |||||
643 | xmmsc_user_data_free_func_t free_f) \ | |||||
644 | { \ | |||||
645 | xmmsc_result_callback_t *cb; \ | |||||
646 | \ | |||||
647 | cb = x_new0 (xmmsc_result_callback_t, 1)calloc (1, sizeof (xmmsc_result_callback_t) * (1)); \ | |||||
648 | if (!cb) { \ | |||||
649 | x_oom()xmmsc_log ("xmmsclient", XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d" , __FUNCTION__, "../src/clients/lib/xmmsclient/result.c", 649 ); \ | |||||
650 | return NULL((void*)0); \ | |||||
651 | } \ | |||||
652 | \ | |||||
653 | cb->type = cbtype; \ | |||||
654 | cb->user_data = udata; \ | |||||
655 | cb->free_func = free_f; \ | |||||
656 | cb->func = f; \ | |||||
657 | \ | |||||
658 | return cb; \ | |||||
659 | } | |||||
660 | ||||||
661 | GEN_RESULT_CALLBACK_NEW_FUNC (default, XMMSC_RESULT_CALLBACK_DEFAULT)static xmmsc_result_callback_t * xmmsc_result_callback_new_default (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f) { xmmsc_result_callback_t *cb; cb = calloc (1, sizeof (xmmsc_result_callback_t) * (1)); if (!cb) { xmmsc_log ("xmmsclient" , XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 661); return ((void *)0); } cb->type = XMMSC_RESULT_CALLBACK_DEFAULT; cb->user_data = udata; cb->free_func = free_f; cb->func = f; return cb ; } | |||||
662 | GEN_RESULT_CALLBACK_NEW_FUNC (raw, XMMSC_RESULT_CALLBACK_RAW)static xmmsc_result_callback_t * xmmsc_result_callback_new_raw (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f) { xmmsc_result_callback_t *cb; cb = calloc (1, sizeof (xmmsc_result_callback_t) * (1)); if (!cb) { xmmsc_log ("xmmsclient" , XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 662); return ((void *)0); } cb->type = XMMSC_RESULT_CALLBACK_RAW; cb->user_data = udata; cb->free_func = free_f; cb->func = f; return cb ; } | |||||
663 | GEN_RESULT_CALLBACK_NEW_FUNC (c2c, XMMSC_RESULT_CALLBACK_C2C)static xmmsc_result_callback_t * xmmsc_result_callback_new_c2c (xmmsc_result_notifier_t f, void *udata, xmmsc_user_data_free_func_t free_f) { xmmsc_result_callback_t *cb; cb = calloc (1, sizeof (xmmsc_result_callback_t) * (1)); if (!cb) { xmmsc_log ("xmmsclient" , XMMS_LOG_LEVEL_FAIL, "Out of memory in %s at %s:%d", __FUNCTION__ , "../src/clients/lib/xmmsclient/result.c", 663); return ((void *)0); } cb->type = XMMSC_RESULT_CALLBACK_C2C; cb->user_data = udata; cb->free_func = free_f; cb->func = f; return cb ; } | |||||
664 | ||||||
665 | /* Add a new notifier to a result | |||||
666 | */ | |||||
667 | static void | |||||
668 | xmmsc_result_notifier_add (xmmsc_result_t *res, xmmsc_result_callback_t *cb) | |||||
669 | { | |||||
670 | /* The pending call takes one ref */ | |||||
671 | xmmsc_result_ref (res); | |||||
672 | res->notifiers = x_list_append (res->notifiers, cb); | |||||
673 | } | |||||
674 | ||||||
675 | ||||||
676 | /* Dereference a notifier from a result. | |||||
677 | * The #x_list_t node containing the notifier is passed. | |||||
678 | */ | |||||
679 | static void | |||||
680 | xmmsc_result_notifier_remove (xmmsc_result_t *res, x_list_t *node) | |||||
681 | { | |||||
682 | free (node->data); /* remove the callback struct, but not the udata */ | |||||
683 | res->notifiers = x_list_delete_link (res->notifiers, node); | |||||
684 | xmmsc_result_unref (res); /* each cb has a reference to res */ | |||||
685 | } | |||||
686 | ||||||
687 | /* Dereference a notifier from a result and delete its udata. | |||||
688 | * The #x_list_t node containing the notifier is passed. | |||||
689 | */ | |||||
690 | static void | |||||
691 | xmmsc_result_notifier_delete (xmmsc_result_t *res, x_list_t *node) | |||||
692 | { | |||||
693 | xmmsc_result_callback_t *cb = node->data; | |||||
694 | ||||||
695 | /* remove the udata */ | |||||
696 | if (cb->free_func) { | |||||
697 | cb->free_func (cb->user_data); | |||||
698 | } | |||||
699 | xmmsc_result_notifier_remove (res, node); | |||||
700 | } | |||||
701 | ||||||
702 | /* Dereference all notifiers from a result and delete their udata. */ | |||||
703 | static void | |||||
704 | xmmsc_result_notifier_delete_all (xmmsc_result_t *res) | |||||
705 | { | |||||
706 | x_list_t *n = res->notifiers; | |||||
707 | ||||||
708 | while (n) { | |||||
709 | x_list_t *next = x_list_next (n)((n) ? (((x_list_t *)(n))->next) : ((void*)0)); | |||||
710 | xmmsc_result_notifier_delete (res, n); | |||||
711 | n = next; | |||||
712 | } | |||||
713 | ||||||
714 | res->notifiers = NULL((void*)0); | |||||
715 | } |