1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
7 /* remote code written after reading xmms remote code.
8 * Copyright (C) 1998-2002 Peter Alm, Mikael Alm, Olle Hallnas,
9 * Thomas Nilsson and 4Front Technologies
10 * Copyright (C) 1999-2002 Haavard Kvaalen
14 #include <sys/types.h>
15 #include <sys/socket.h>
20 #include "marshalers.h"
25 /*** remote protocol structure ***/
26 #define REMOTE_PROTOCOL_ACK 'A'
27 #define REMOTE_PROTOCOL_PRESENT 'P'
28 #define REMOTE_PROTOCOL_CHANGE_USER 'U'
36 /*** LogJamRemote object ***/
37 struct _LogJamRemote
{
43 struct _LogJamRemoteClass
{
44 GObjectClass parent_class
;
45 void (*present
)(LogJamRemote
*remote
);
46 gboolean (*change_user
)(LogJamRemote
*remote
,
47 char* username
, gboolean skip
, GError
**err
);
56 static guint signals
[LAST_SIGNAL
] = { 0 };
59 logjam_remote_finalize(GObject
*object
) {
60 GObjectClass
*parent_class
;
62 logjam_remote_stop_listening((LogJamRemote
*)object
, NULL
);
64 parent_class
= g_type_class_peek_parent(G_OBJECT_GET_CLASS(object
));
65 parent_class
->finalize(object
);
69 logjam_remote_class_init(gpointer klass
, gpointer class_data
) {
70 GObjectClass
*gclass
= G_OBJECT_CLASS(klass
);
72 gclass
->finalize
= logjam_remote_finalize
;
74 signals
[PRESENT
] = g_signal_new("present",
75 G_OBJECT_CLASS_TYPE(gclass
),
77 G_STRUCT_OFFSET(LogJamRemoteClass
, present
),
79 g_cclosure_marshal_VOID__VOID
,
82 signals
[CHANGE_USER
] = g_signal_new("change_user",
83 G_OBJECT_CLASS_TYPE(gclass
),
85 G_STRUCT_OFFSET(LogJamRemoteClass
, change_user
),
87 logjam_marshal_BOOLEAN__STRING_POINTER
,
88 G_TYPE_BOOLEAN
, 2, G_TYPE_STRING
, G_TYPE_POINTER
);
92 logjam_remote_init(GTypeInstance
*inst
, gpointer g_class
) {
93 LogJamRemote
*remote
= (LogJamRemote
*)inst
;
98 logjam_remote_get_type() {
99 static GType remote_type
= 0;
101 static const GTypeInfo remote_info
= {
102 sizeof(LogJamRemoteClass
),
105 logjam_remote_class_init
,
108 sizeof(LogJamRemote
),
112 remote_type
= g_type_register_static(G_TYPE_OBJECT
, "LogJamRemote",
119 logjam_remote_new(void) {
120 return g_object_new(logjam_remote_get_type(), NULL
);
124 remote_error_quark(void) {
125 static GQuark quark
= 0;
127 quark
= g_quark_from_static_string("remote-error-quark");
132 make_remote_path(char *buf
, int len
) {
133 g_snprintf(buf
, len
, "%s/remote", app
.conf_dir
);
137 remote_connect(GError
**err
) {
139 struct sockaddr_un saddr
;
141 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
142 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
143 "socket: %s", g_strerror(errno
));
147 saddr
.sun_family
= AF_UNIX
;
148 make_remote_path(saddr
.sun_path
, 100);
149 if (connect(fd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) < 0) {
151 if (errno
!= ENOENT
&& errno
!= ECONNREFUSED
) {
152 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
153 "connect: %s", g_strerror(errno
));
155 /* ENOENT is not a real error; there was just nobody there. */
163 remote_send(char command
, gpointer data
, int datalen
, GError
**err
) {
167 if ((fd
= remote_connect(err
)) < 0)
170 len
= write(fd
, &command
, 1);
172 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
173 "write: %s", g_strerror(errno
));
177 len
= write(fd
, data
, datalen
);
179 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
180 "write: %s", g_strerror(errno
));
185 len
= read(fd
, &response
, 1);
187 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
188 "read: %s", g_strerror(errno
));
194 if (response
== REMOTE_PROTOCOL_ACK
)
200 remote_send_user(const char *username
, GError
**err
) {
202 RemoteChangeLJUser user
;
203 strcpy(user
.username
, username
);
205 return remote_send(REMOTE_PROTOCOL_CHANGE_USER
,
206 &user
, sizeof(RemoteChangeLJUser
), err
);
208 return remote_send(REMOTE_PROTOCOL_PRESENT
, NULL
, 0, err
);
213 remote_recieve_change_user(LogJamRemote
*remote
, int fd
) {
214 RemoteChangeLJUser user
;
219 readret
= read(fd
, &user
, sizeof(RemoteChangeLJUser
));
221 /*jam_warning(win, _("Accepting remote command: %s: %s."),
222 "read", g_strerror(errno));*/
227 g_signal_emit_by_name(remote
, "change_user",
228 user
.username
, &err
, &signalret
);
233 io_cb(GIOChannel
*channel
, GIOCondition cond
, gpointer data
) {
234 LogJamRemote
*remote
= (LogJamRemote
*)data
;
237 struct sockaddr_un saddr
;
238 socklen_t addrlen
= sizeof(struct sockaddr_un
);
240 if (cond
== G_IO_IN
) {
241 fd
= accept(remote
->fd
, (struct sockaddr
*)&saddr
, &addrlen
);
243 /*jam_warning(win, _("Accepting remote command: %s: %s."),
244 "accept", g_strerror(errno));*/
248 ret
= read(fd
, &command
, 1);
250 /*jam_warning(win, _("Accepting remote command: %s: %s."),
251 "read", g_strerror(errno));*/
257 case REMOTE_PROTOCOL_PRESENT
:
258 g_signal_emit_by_name(remote
, "present");
260 case REMOTE_PROTOCOL_CHANGE_USER
:
261 if (!remote_recieve_change_user(remote
, fd
))
265 //jam_warning(win, _("Unknown remote command (%c)."), command);
270 command
= REMOTE_PROTOCOL_ACK
;
271 write(fd
, &command
, 1);
279 logjam_remote_listen(LogJamRemote
*remote
, GError
**err
) {
280 struct sockaddr_un saddr
;
286 if ((remote
->fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
287 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
288 "socket: %s", g_strerror(errno
));
292 saddr
.sun_family
= AF_UNIX
;
293 make_remote_path(saddr
.sun_path
, 100);
294 unlink(saddr
.sun_path
);
296 if (bind(remote
->fd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) < 0) {
299 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
300 "bind: %s", g_strerror(errno
));
304 if (listen(remote
->fd
, 1) < 0) {
307 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
,
308 "listen: %s", g_strerror(errno
));
312 channel
= g_io_channel_unix_new(remote
->fd
);
313 g_io_channel_set_encoding(channel
, NULL
, NULL
);
314 g_io_channel_set_buffered(channel
, FALSE
);
315 g_io_add_watch(channel
, G_IO_IN
|G_IO_ERR
,
317 g_io_channel_unref(channel
);
323 logjam_remote_stop_listening(LogJamRemote
*remote
, GError
**err
) {
332 make_remote_path(buf
, 128);