1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
5 /* remote code written after reading xmms remote code.
6 * Copyright (C) 1998-2002 Peter Alm, Mikael Alm, Olle Hallnas,
7 * Thomas Nilsson and 4Front Technologies
8 * Copyright (C) 1999-2002 Haavard Kvaalen
15 #include <sys/socket.h>
16 #include <sys/types.h>
20 #include "marshalers.h"
24 /*** remote protocol structure ***/
25 #define REMOTE_PROTOCOL_ACK 'A'
26 #define REMOTE_PROTOCOL_PRESENT 'P'
27 #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
, char *username
, gboolean skip
, GError
**err
);
56 static guint signals
[LAST_SIGNAL
] = { 0 };
58 static void logjam_remote_finalize(GObject
*object
) {
59 GObjectClass
*parent_class
;
60 logjam_remote_stop_listening((LogJamRemote
*) object
, NULL
);
61 parent_class
= g_type_class_peek_parent(G_OBJECT_GET_CLASS(object
));
62 parent_class
->finalize(object
);
66 static void logjam_remote_class_init (gpointer klass
, gpointer class_data
) {
67 GObjectClass
*gclass
= G_OBJECT_CLASS(klass
);
68 gclass
->finalize
= logjam_remote_finalize
;
69 signals
[PRESENT
] = g_signal_new("present",
70 G_OBJECT_CLASS_TYPE(gclass
),
72 G_STRUCT_OFFSET(LogJamRemoteClass
, present
), NULL
, NULL
, g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
73 signals
[CHANGE_USER
] = g_signal_new("change_user",
74 G_OBJECT_CLASS_TYPE(gclass
),
76 G_STRUCT_OFFSET(LogJamRemoteClass
, change_user
),
77 NULL
, NULL
, logjam_marshal_BOOLEAN__STRING_POINTER
, G_TYPE_BOOLEAN
, 2, G_TYPE_STRING
, G_TYPE_POINTER
);
81 static void logjam_remote_init (GTypeInstance
*inst
, gpointer g_class
) {
82 LogJamRemote
*remote
= (LogJamRemote
*)inst
;
87 GType
logjam_remote_get_type (void) {
88 static GType remote_type
= 0;
90 static const GTypeInfo remote_info
= {
91 sizeof(LogJamRemoteClass
),
94 logjam_remote_class_init
,
101 remote_type
= g_type_register_static(G_TYPE_OBJECT
, "LogJamRemote", &remote_info
, 0);
107 LogJamRemote
*logjam_remote_new (void) {
108 return g_object_new(logjam_remote_get_type(), NULL
);
112 GQuark
remote_error_quark (void) {
113 static GQuark quark
= 0;
114 if (quark
== 0) quark
= g_quark_from_static_string("remote-error-quark");
119 static void make_remote_path (char *buf
, int len
) {
120 g_snprintf(buf
, len
, "%s/remote", app
.conf_dir
);
124 static int remote_connect (GError
**err
) {
126 struct sockaddr_un saddr
;
128 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
129 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "socket: %s", g_strerror(errno
));
133 saddr
.sun_family
= AF_UNIX
;
134 make_remote_path(saddr
.sun_path
, 100);
135 if (connect(fd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) < 0) {
137 if (errno
!= ENOENT
&& errno
!= ECONNREFUSED
) {
138 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "connect: %s", g_strerror(errno
));
140 /* ENOENT is not a real error; there was just nobody there. */
148 static gboolean
remote_send (char command
, gpointer data
, int datalen
, GError
**err
) {
152 if ((fd
= remote_connect(err
)) < 0) return FALSE
;
154 len
= write(fd
, &command
, 1);
156 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "write: %s", g_strerror(errno
));
160 len
= write(fd
, data
, datalen
);
162 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "write: %s", g_strerror(errno
));
167 len
= read(fd
, &response
, 1);
169 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "read: %s", g_strerror(errno
));
172 if (len
< 1) return FALSE
;
174 if (response
== REMOTE_PROTOCOL_ACK
) return TRUE
;
180 gboolean
remote_send_user (const char *username
, GError
**err
) {
182 RemoteChangeLJUser user
;
183 strcpy(user
.username
, username
);
185 return remote_send(REMOTE_PROTOCOL_CHANGE_USER
, &user
, sizeof(RemoteChangeLJUser
), err
);
187 return remote_send(REMOTE_PROTOCOL_PRESENT
, NULL
, 0, err
);
191 static gboolean
remote_recieve_change_user (LogJamRemote
*remote
, int fd
) {
192 RemoteChangeLJUser user
;
197 readret
= read(fd
, &user
, sizeof(RemoteChangeLJUser
));
199 /*jam_warning(win, _("Accepting remote command: %s: %s."), "read", g_strerror(errno));*/
204 g_signal_emit_by_name(remote
, "change_user", user
.username
, &err
, &signalret
);
209 static gboolean
io_cb (GIOChannel
*channel
, GIOCondition cond
, gpointer data
) {
210 LogJamRemote
*remote
= (LogJamRemote
*) data
;
213 struct sockaddr_un saddr
;
214 socklen_t addrlen
= sizeof(struct sockaddr_un
);
216 if (cond
== G_IO_IN
) {
217 fd
= accept(remote
->fd
, (struct sockaddr
*)&saddr
, &addrlen
);
219 /*jam_warning(win, _("Accepting remote command: %s: %s."), "accept", g_strerror(errno));*/
223 ret
= read(fd
, &command
, 1);
225 /*jam_warning(win, _("Accepting remote command: %s: %s."), "read", g_strerror(errno));*/
231 case REMOTE_PROTOCOL_PRESENT
:
232 g_signal_emit_by_name(remote
, "present");
234 case REMOTE_PROTOCOL_CHANGE_USER
:
235 if (!remote_recieve_change_user(remote
, fd
)) return TRUE
;
238 //jam_warning(win, _("Unknown remote command (%c)."), command);
243 command
= REMOTE_PROTOCOL_ACK
;
244 write(fd
, &command
, 1);
252 gboolean
logjam_remote_listen (LogJamRemote
*remote
, GError
**err
) {
253 struct sockaddr_un saddr
;
256 if (remote
->fd
> 0) return TRUE
;
258 if ((remote
->fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
259 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "socket: %s", g_strerror(errno
));
263 saddr
.sun_family
= AF_UNIX
;
264 make_remote_path(saddr
.sun_path
, 100);
265 unlink(saddr
.sun_path
);
267 if (bind(remote
->fd
, (struct sockaddr
*)&saddr
, sizeof(saddr
)) < 0) {
270 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "bind: %s", g_strerror(errno
));
274 if (listen(remote
->fd
, 1) < 0) {
277 g_set_error(err
, REMOTE_ERROR
, REMOTE_ERROR_SYSTEM
, "listen: %s", g_strerror(errno
));
281 channel
= g_io_channel_unix_new(remote
->fd
);
282 g_io_channel_set_encoding(channel
, NULL
, NULL
);
283 g_io_channel_set_buffered(channel
, FALSE
);
284 g_io_add_watch(channel
, G_IO_IN
| G_IO_ERR
, io_cb
, remote
);
285 g_io_channel_unref(channel
);
291 gboolean
logjam_remote_stop_listening (LogJamRemote
*remote
, GError
**err
) {
293 if (remote
->fd
<= 0) return TRUE
;
296 make_remote_path(buf
, 128);