Mercurial > pidgin
comparison src/core.c @ 5859:022786c7ab53
[gaim-migrate @ 6290]
CUI is gone, long live gaim-remote! The old CUI functionality, which was
for remote-controlling gaim, is now a Core Plugin, so any future UI
(including the current, normal gaim gtk UI) can be remote-controlled.
Applications will soon be able to link against the library and header files
and provide their own remote-control of gaim, but why bother? :) If you
use gaim-remote, make sure to load the new plugin. It won't auto-load.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sat, 14 Jun 2003 06:06:40 +0000 |
parents | 86456ec3ca25 |
children | 059d95c67cda |
comparison
equal
deleted
inserted
replaced
5858:96e5b32e75ad | 5859:022786c7ab53 |
---|---|
1 /* | 1 /* |
2 * gaim | 2 * gaim |
3 * | 3 * |
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> | |
5 * | 4 * |
6 * This program is free software; you can redistribute it and/or modify | 5 * This program is free software; you can redistribute it and/or modify |
7 * it under the terms of the GNU General Public License as published by | 6 * it under the terms of the GNU General Public License as published by |
8 * the Free Software Foundation; either version 2 of the License, or | 7 * the Free Software Foundation; either version 2 of the License, or |
9 * (at your option) any later version. | 8 * (at your option) any later version. |
26 #include <glib.h> | 25 #include <glib.h> |
27 #include <stdio.h> | 26 #include <stdio.h> |
28 #include <stdlib.h> | 27 #include <stdlib.h> |
29 #include <sys/types.h> | 28 #include <sys/types.h> |
30 | 29 |
31 #ifdef _WIN32 | |
32 #include <winsock.h> | |
33 #include <io.h> | |
34 #else | |
35 #include <sys/socket.h> | |
36 #include <sys/un.h> | |
37 #include <unistd.h> | |
38 #endif | |
39 | 30 |
40 #include <sys/stat.h> | |
41 #include <errno.h> | |
42 #include <signal.h> | |
43 #include <getopt.h> | |
44 #include <stdarg.h> | |
45 #include <string.h> | |
46 | |
47 #include "gaim.h" | |
48 #include "gaim-socket.h" | |
49 | |
50 #ifdef _WIN32 | |
51 #include "win32dep.h" | |
52 #endif | |
53 | |
54 #ifndef _WIN32 | |
55 static gint UI_fd = -1; | |
56 #endif | |
57 int gaim_session = 0; | |
58 GSList *uis = NULL; | |
59 | |
60 static guchar *UI_build(guint32 *len, guchar type, guchar subtype, va_list args) | |
61 { | |
62 guchar *buffer; | |
63 guint32 pos; | |
64 int size; | |
65 void *data; | |
66 | |
67 *len = sizeof(guchar) * 2 + 4; | |
68 buffer = g_malloc(*len); | |
69 pos = 0; | |
70 | |
71 memcpy(buffer + pos, &type, sizeof(type)); pos += sizeof(type); | |
72 memcpy(buffer + pos, &subtype, sizeof(subtype)); pos += sizeof(subtype); | |
73 | |
74 /* we come back and do size last */ | |
75 pos += 4; | |
76 | |
77 size = va_arg(args, int); | |
78 while (size != -1) { | |
79 *len += size; | |
80 buffer = g_realloc(buffer, *len); | |
81 | |
82 data = va_arg(args, void *); | |
83 memcpy(buffer + pos, data, size); | |
84 pos += size; | |
85 | |
86 size = va_arg(args, int); | |
87 } | |
88 | |
89 pos -= sizeof(guchar) * 2 + 4; | |
90 | |
91 /* now we do size */ | |
92 memcpy(buffer + sizeof(guchar) * 2, &pos, 4); | |
93 | |
94 return buffer; | |
95 } | |
96 | |
97 gint UI_write(struct UI *ui, guchar *data, gint len) | |
98 { | |
99 GError *error = NULL; | |
100 gint sent; | |
101 /* we'll let the write silently fail because the read will pick it up as dead */ | |
102 g_io_channel_write_chars(ui->channel, data, len, &sent, &error); | |
103 if (error) | |
104 g_error_free(error); | |
105 return sent; | |
106 } | |
107 | |
108 void UI_build_write(struct UI *ui, guchar type, guchar subtype, ...) | |
109 { | |
110 va_list ap; | |
111 gchar *data; | |
112 guint32 len; | |
113 | |
114 va_start(ap, subtype); | |
115 data = UI_build(&len, type, subtype, ap); | |
116 va_end(ap); | |
117 | |
118 UI_write(ui, data, len); | |
119 | |
120 g_free(data); | |
121 } | |
122 | |
123 void UI_broadcast(guchar *data, gint len) | |
124 { | |
125 GSList *u = uis; | |
126 while (u) { | |
127 struct UI *ui = u->data; | |
128 UI_write(ui, data, len); | |
129 u = u->next; | |
130 } | |
131 } | |
132 | |
133 void UI_build_broadcast(guchar type, guchar subtype, ...) | |
134 { | |
135 va_list ap; | |
136 gchar *data; | |
137 guint32 len; | |
138 | |
139 if (!uis) | |
140 return; | |
141 | |
142 va_start(ap, subtype); | |
143 data = UI_build(&len, type, subtype, ap); | |
144 va_end(ap); | |
145 | |
146 UI_broadcast(data, len); | |
147 | |
148 g_free(data); | |
149 } | |
150 | |
151 #ifndef _WIN32 | |
152 static void meta_handler(struct UI *ui, guchar subtype, guchar *data) | |
153 { | |
154 struct gaim_cui_packet *p; | |
155 GError *error = NULL; | |
156 switch (subtype) { | |
157 case CUI_META_LIST: | |
158 break; | |
159 case CUI_META_QUIT: | |
160 while (uis) { | |
161 ui = uis->data; | |
162 uis = g_slist_remove(uis, ui); | |
163 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
164 g_source_remove(ui->inpa); | |
165 g_free(ui); | |
166 } | |
167 do_quit(); | |
168 break; | |
169 case CUI_META_DETACH: | |
170 uis = g_slist_remove(uis, ui); | |
171 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
172 g_source_remove(ui->inpa); | |
173 g_free(ui); | |
174 break; | |
175 case CUI_META_PING: | |
176 p = cui_packet_new(CUI_TYPE_META, CUI_META_ACK); | |
177 cui_send_packet(g_io_channel_unix_get_fd(ui->channel), p); | |
178 cui_packet_free(p); | |
179 break; | |
180 default: | |
181 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
182 "Unhandled meta subtype %d\n", subtype); | |
183 break; | |
184 } | |
185 | |
186 if(error) | |
187 g_error_free(error); | |
188 } | |
189 | |
190 static void plugin_handler(struct UI *ui, guchar subtype, guchar *data) | |
191 { | |
192 #ifdef GAIM_PLUGINS | |
193 guint id; | |
194 GaimPlugin *p; | |
195 | |
196 switch (subtype) { | |
197 /* | |
198 case CUI_PLUGIN_LIST: | |
199 break; | |
200 */ | |
201 case CUI_PLUGIN_LOAD: | |
202 gaim_plugin_load(gaim_plugin_probe(data)); | |
203 break; | |
204 case CUI_PLUGIN_UNLOAD: | |
205 memcpy(&id, data, sizeof(id)); | |
206 p = g_list_nth_data(gaim_plugins_get_loaded(), id); | |
207 if (p) { | |
208 gaim_plugin_unload(p); | |
209 } | |
210 break; | |
211 default: | |
212 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
213 "Unhandled plugin subtype %d\n", subtype); | |
214 break; | |
215 } | |
216 #endif | |
217 } | |
218 | |
219 static void user_handler(struct UI *ui, guchar subtype, guchar *data) | |
220 { | |
221 guint id; | |
222 GaimAccount *account; | |
223 | |
224 switch (subtype) { | |
225 /* | |
226 case CUI_USER_LIST: | |
227 break; | |
228 case CUI_USER_ADD: | |
229 break; | |
230 case CUI_USER_REMOVE: | |
231 break; | |
232 case CUI_USER_MODIFY: | |
233 break; | |
234 */ | |
235 case CUI_USER_SIGNON: | |
236 if (!data) | |
237 return; | |
238 memcpy(&id, data, sizeof(id)); | |
239 account = g_list_nth_data(gaim_accounts_get_all(), id); | |
240 if (account) | |
241 serv_login(account); | |
242 /* don't need to do anything here because the UI will get updates from other handlers */ | |
243 break; | |
244 default: | |
245 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
246 "Unhandled user subtype %d\n", subtype); | |
247 break; | |
248 } | |
249 } | |
250 | |
251 static void message_handler(struct UI *ui, guchar subtype, guchar *data) | |
252 { | |
253 switch (subtype) { | |
254 case CUI_MESSAGE_LIST: | |
255 break; | |
256 case CUI_MESSAGE_SEND: | |
257 if (!data) | |
258 return; | |
259 { | |
260 guint id; | |
261 GaimConnection *gc; | |
262 guint len; | |
263 char *who, *msg; | |
264 gint flags; | |
265 int pos = 0; | |
266 | |
267 memcpy(&id, data + pos, sizeof(id)); | |
268 pos += sizeof(id); | |
269 gc = g_list_nth_data(gaim_connections_get_all(), id); | |
270 if (!gc) | |
271 return; | |
272 | |
273 memcpy(&len, data + pos, sizeof(len)); | |
274 pos += sizeof(len); | |
275 who = g_strndup(data + pos, len + 1); | |
276 pos += len; | |
277 | |
278 memcpy(&len, data + pos, sizeof(len)); | |
279 pos += sizeof(len); | |
280 msg = g_strndup(data + pos, len + 1); | |
281 pos += len; | |
282 | |
283 memcpy(&flags, data + pos, sizeof(flags)); | |
284 serv_send_im(gc, who, msg, -1, flags); | |
285 | |
286 g_free(who); | |
287 g_free(msg); | |
288 } | |
289 break; | |
290 case CUI_MESSAGE_RECV: | |
291 break; | |
292 default: | |
293 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
294 "Unhandled message subtype %d\n", subtype); | |
295 break; | |
296 } | |
297 } | |
298 | |
299 static gint gaim_recv(GIOChannel *source, guchar *buf, gint len) | |
300 { | |
301 gint total = 0; | |
302 gint cur; | |
303 | |
304 GError *error = NULL; | |
305 | |
306 while (total < len) { | |
307 if (g_io_channel_read_chars(source, buf + total, len - total, &cur, &error) != G_IO_STATUS_NORMAL) { | |
308 if (error) | |
309 g_error_free(error); | |
310 return -1; | |
311 } | |
312 if (cur == 0) | |
313 return total; | |
314 total += cur; | |
315 } | |
316 | |
317 return total; | |
318 } | |
319 | |
320 static void remote_handler(struct UI *ui, guchar subtype, guchar *data, int len) | |
321 { | |
322 const char *resp; | |
323 char *send; | |
324 switch (subtype) { | |
325 case CUI_REMOTE_CONNECTIONS: | |
326 break; | |
327 case CUI_REMOTE_URI: | |
328 send = g_malloc(len + 1); | |
329 memcpy(send, data, len); | |
330 send[len] = 0; | |
331 resp = handle_uri(send); | |
332 g_free(send); | |
333 /* report error */ | |
334 break; | |
335 default: | |
336 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
337 "Unhandled remote subtype %d\n", subtype); | |
338 break; | |
339 } | |
340 } | |
341 | |
342 static gboolean UI_readable(GIOChannel *source, GIOCondition cond, gpointer data) | |
343 { | |
344 struct UI *ui = data; | |
345 | |
346 guchar type; | |
347 guchar subtype; | |
348 guint32 len; | |
349 | |
350 GError *error = NULL; | |
351 | |
352 guchar *in; | |
353 | |
354 /* no byte order worries! this'll change if we go to TCP */ | |
355 if (gaim_recv(source, &type, sizeof(type)) != sizeof(type)) { | |
356 gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n"); | |
357 uis = g_slist_remove(uis, ui); | |
358 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
359 if(error) { | |
360 g_error_free(error); | |
361 error = NULL; | |
362 } | |
363 g_source_remove(ui->inpa); | |
364 g_free(ui); | |
365 return FALSE; | |
366 } | |
367 | |
368 if (gaim_recv(source, &subtype, sizeof(subtype)) != sizeof(subtype)) { | |
369 gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n"); | |
370 uis = g_slist_remove(uis, ui); | |
371 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
372 if(error) { | |
373 g_error_free(error); | |
374 error = NULL; | |
375 } | |
376 g_source_remove(ui->inpa); | |
377 g_free(ui); | |
378 return FALSE; | |
379 } | |
380 | |
381 if (gaim_recv(source, (guchar *)&len, sizeof(len)) != sizeof(len)) { | |
382 gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n"); | |
383 uis = g_slist_remove(uis, ui); | |
384 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
385 if(error) { | |
386 g_error_free(error); | |
387 error = NULL; | |
388 } | |
389 g_source_remove(ui->inpa); | |
390 g_free(ui); | |
391 return FALSE; | |
392 } | |
393 | |
394 if (len) { | |
395 in = g_new0(guchar, len); | |
396 if (gaim_recv(source, in, len) != len) { | |
397 gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n"); | |
398 uis = g_slist_remove(uis, ui); | |
399 g_io_channel_shutdown(ui->channel, TRUE, &error); | |
400 if(error) { | |
401 g_error_free(error); | |
402 error = NULL; | |
403 } | |
404 g_source_remove(ui->inpa); | |
405 g_free(ui); | |
406 return FALSE; | |
407 } | |
408 } else | |
409 in = NULL; | |
410 | |
411 switch (type) { | |
412 case CUI_TYPE_META: | |
413 meta_handler(ui, subtype, in); | |
414 break; | |
415 case CUI_TYPE_PLUGIN: | |
416 plugin_handler(ui, subtype, in); | |
417 break; | |
418 case CUI_TYPE_USER: | |
419 user_handler(ui, subtype, in); | |
420 break; | |
421 /* | |
422 case CUI_TYPE_CONN: | |
423 conn_handler(ui, subtype, in); | |
424 break; | |
425 case CUI_TYPE_BUDDY: | |
426 buddy_handler(ui, subtype, in); | |
427 break; | |
428 */ | |
429 case CUI_TYPE_MESSAGE: | |
430 message_handler(ui, subtype, in); | |
431 break; | |
432 /* | |
433 case CUI_TYPE_CHAT: | |
434 chat_handler(ui, subtype, in); | |
435 break; | |
436 */ | |
437 case CUI_TYPE_REMOTE: | |
438 remote_handler(ui, subtype, in, len); | |
439 break; | |
440 default: | |
441 gaim_debug(GAIM_DEBUG_WARNING, "cui", | |
442 "Unhandled type %d\n", type); | |
443 break; | |
444 } | |
445 | |
446 if (in) | |
447 g_free(in); | |
448 return TRUE; | |
449 } | |
450 | |
451 static gboolean socket_readable(GIOChannel *source, GIOCondition cond, gpointer data) | |
452 { | |
453 struct sockaddr_un saddr; | |
454 gint len = sizeof(saddr); | |
455 gint fd; | |
456 | |
457 struct UI *ui; | |
458 | |
459 if ((fd = accept(UI_fd, (struct sockaddr *)&saddr, &len)) == -1) | |
460 return FALSE; | |
461 | |
462 ui = g_new0(struct UI, 1); | |
463 uis = g_slist_append(uis, ui); | |
464 | |
465 ui->channel = g_io_channel_unix_new(fd); | |
466 ui->inpa = g_io_add_watch(ui->channel, G_IO_IN | G_IO_HUP | G_IO_ERR, UI_readable, ui); | |
467 g_io_channel_unref(ui->channel); | |
468 | |
469 gaim_debug(GAIM_DEBUG_MISC, "cui", "Got one\n"); | |
470 return TRUE; | |
471 } | |
472 | |
473 static gint open_socket() | |
474 { | |
475 struct sockaddr_un saddr; | |
476 gint fd; | |
477 | |
478 while (gaim_session_exists(gaim_session)) | |
479 gaim_session++; | |
480 | |
481 gaim_debug(GAIM_DEBUG_MISC, "cui", "Session: %d\n", gaim_session); | |
482 | |
483 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) { | |
484 mode_t m = umask(0177); | |
485 saddr.sun_family = AF_UNIX; | |
486 | |
487 g_snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s" G_DIR_SEPARATOR_S "gaim_%s.%d", | |
488 g_get_tmp_dir(), g_get_user_name(), gaim_session); | |
489 if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) != -1) | |
490 listen(fd, 100); | |
491 else { | |
492 g_log(NULL, G_LOG_LEVEL_CRITICAL, | |
493 "Failed to assign %s to a socket (Error: %s)", | |
494 saddr.sun_path, strerror(errno)); | |
495 return -1; | |
496 } | |
497 umask(m); | |
498 } else | |
499 g_log(NULL, G_LOG_LEVEL_CRITICAL, "Unable to open socket: %s", strerror(errno)); | |
500 return fd; | |
501 } | |
502 #endif /*! _WIN32*/ | |
503 | |
504 int core_main() | |
505 { | |
506 /* | |
507 GMainLoop *loop; | |
508 */ | |
509 #ifndef _WIN32 | |
510 GIOChannel *channel; | |
511 #endif | |
512 | |
513 gaim_set_blist(gaim_blist_new()); | |
514 gaim_blist_load(); | |
515 | |
516 #ifndef _WIN32 | |
517 | |
518 UI_fd = open_socket(); | |
519 if (UI_fd < 0) | |
520 return 1; | |
521 | |
522 channel = g_io_channel_unix_new(UI_fd); | |
523 g_io_add_watch(channel, G_IO_IN, socket_readable, NULL); | |
524 g_io_channel_unref(channel); | |
525 #endif | |
526 | |
527 /* | |
528 loop = g_main_new(TRUE); | |
529 g_main_run(loop); | |
530 */ | |
531 | |
532 return 0; | |
533 } | |
534 | |
535 void core_quit() | |
536 { | |
537 /* don't save prefs after plugins are gone... */ | |
538 #ifndef _WIN32 | |
539 char buf[1024]; | |
540 close(UI_fd); | |
541 snprintf(buf, 1024, "%s" G_DIR_SEPARATOR_S "gaim_%s.%d", | |
542 g_get_tmp_dir(), g_get_user_name(), gaim_session); | |
543 | |
544 unlink(buf); | |
545 | |
546 gaim_debug(GAIM_DEBUG_MISC, "core", "Removed core\n"); | |
547 #endif | |
548 } |