progress.*: cosmetix
[k8lowj.git] / src / remote.c
blobc80d00caaa72c98c769d2fa2ee22c2caffc77a65
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
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
13 #include "gtk-all.h"
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <unistd.h>
18 #include <errno.h>
20 #include "marshalers.h"
22 #include "conf.h"
23 #include "remote.h"
25 /*** remote protocol structure ***/
26 #define REMOTE_PROTOCOL_ACK 'A'
27 #define REMOTE_PROTOCOL_PRESENT 'P'
28 #define REMOTE_PROTOCOL_CHANGE_USER 'U'
30 typedef struct {
31 char username[50];
32 gboolean login;
33 } RemoteChangeLJUser;
36 /*** LogJamRemote object ***/
37 struct _LogJamRemote {
38 GObject parent;
40 int fd;
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);
50 enum {
51 PRESENT,
52 CHANGE_USER,
53 LAST_SIGNAL
56 static guint signals[LAST_SIGNAL] = { 0 };
58 static void
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);
68 static void
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),
76 G_SIGNAL_RUN_LAST,
77 G_STRUCT_OFFSET(LogJamRemoteClass, present),
78 NULL, NULL,
79 g_cclosure_marshal_VOID__VOID,
80 G_TYPE_NONE, 0);
82 signals[CHANGE_USER] = g_signal_new("change_user",
83 G_OBJECT_CLASS_TYPE(gclass),
84 G_SIGNAL_RUN_LAST,
85 G_STRUCT_OFFSET(LogJamRemoteClass, change_user),
86 NULL, NULL,
87 logjam_marshal_BOOLEAN__STRING_POINTER,
88 G_TYPE_BOOLEAN, 2, G_TYPE_STRING, G_TYPE_POINTER);
91 static void
92 logjam_remote_init(GTypeInstance *inst, gpointer g_class) {
93 LogJamRemote *remote = (LogJamRemote*)inst;
94 remote->fd = -1;
97 GType
98 logjam_remote_get_type() {
99 static GType remote_type = 0;
100 if (!remote_type) {
101 static const GTypeInfo remote_info = {
102 sizeof(LogJamRemoteClass),
103 NULL,
104 NULL,
105 logjam_remote_class_init,
106 NULL,
107 NULL,
108 sizeof(LogJamRemote),
110 logjam_remote_init
112 remote_type = g_type_register_static(G_TYPE_OBJECT, "LogJamRemote",
113 &remote_info, 0);
115 return remote_type;
118 LogJamRemote*
119 logjam_remote_new(void) {
120 return g_object_new(logjam_remote_get_type(), NULL);
123 GQuark
124 remote_error_quark(void) {
125 static GQuark quark = 0;
126 if (quark == 0)
127 quark = g_quark_from_static_string("remote-error-quark");
128 return quark;
131 static void
132 make_remote_path(char *buf, int len) {
133 g_snprintf(buf, len, "%s/remote", app.conf_dir);
136 static int
137 remote_connect(GError **err) {
138 int fd;
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));
144 return -1;
147 saddr.sun_family = AF_UNIX;
148 make_remote_path(saddr.sun_path, 100);
149 if (connect(fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
150 close(fd);
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. */
156 return -1;
159 return fd;
162 static gboolean
163 remote_send(char command, gpointer data, int datalen, GError **err) {
164 int fd, len;
165 char response;
167 if ((fd = remote_connect(err)) < 0)
168 return FALSE;
170 len = write(fd, &command, 1);
171 if (len < 0) {
172 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM,
173 "write: %s", g_strerror(errno));
174 return FALSE;
176 if (datalen > 0) {
177 len = write(fd, data, datalen);
178 if (len < 0) {
179 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM,
180 "write: %s", g_strerror(errno));
181 return FALSE;
185 len = read(fd, &response, 1);
186 if (len < 0) {
187 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM,
188 "read: %s", g_strerror(errno));
189 return FALSE;
191 if (len < 1)
192 return FALSE;
194 if (response == REMOTE_PROTOCOL_ACK)
195 return TRUE;
196 return FALSE;
199 gboolean
200 remote_send_user(const char *username, GError **err) {
201 if (username) {
202 RemoteChangeLJUser user;
203 strcpy(user.username, username);
204 user.login = TRUE;
205 return remote_send(REMOTE_PROTOCOL_CHANGE_USER,
206 &user, sizeof(RemoteChangeLJUser), err);
207 } else {
208 return remote_send(REMOTE_PROTOCOL_PRESENT, NULL, 0, err);
212 static gboolean
213 remote_recieve_change_user(LogJamRemote *remote, int fd) {
214 RemoteChangeLJUser user;
215 int readret;
216 gboolean signalret;
217 GError *err = NULL;
219 readret = read(fd, &user, sizeof(RemoteChangeLJUser));
220 if (readret < 0) {
221 /*jam_warning(win, _("Accepting remote command: %s: %s."),
222 "read", g_strerror(errno));*/
223 close(fd);
224 return FALSE;
227 g_signal_emit_by_name(remote, "change_user",
228 user.username, &err, &signalret);
229 return TRUE;
232 static gboolean
233 io_cb(GIOChannel *channel, GIOCondition cond, gpointer data) {
234 LogJamRemote *remote = (LogJamRemote*)data;
235 char command;
236 int fd, ret;
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);
242 if (fd < 0) {
243 /*jam_warning(win, _("Accepting remote command: %s: %s."),
244 "accept", g_strerror(errno));*/
245 return TRUE;
248 ret = read(fd, &command, 1);
249 if (ret < 0) {
250 /*jam_warning(win, _("Accepting remote command: %s: %s."),
251 "read", g_strerror(errno));*/
252 close(fd);
253 return TRUE;
256 switch (command) {
257 case REMOTE_PROTOCOL_PRESENT:
258 g_signal_emit_by_name(remote, "present");
259 break;
260 case REMOTE_PROTOCOL_CHANGE_USER:
261 if (!remote_recieve_change_user(remote, fd))
262 return TRUE;
263 break;
264 default:
265 //jam_warning(win, _("Unknown remote command (%c)."), command);
266 close(fd);
267 return TRUE;
270 command = REMOTE_PROTOCOL_ACK;
271 write(fd, &command, 1);
272 close(fd);
275 return TRUE;
278 gboolean
279 logjam_remote_listen(LogJamRemote *remote, GError **err) {
280 struct sockaddr_un saddr;
281 GIOChannel *channel;
283 if (remote->fd > 0)
284 return TRUE;
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));
289 return FALSE;
292 saddr.sun_family = AF_UNIX;
293 make_remote_path(saddr.sun_path, 100);
294 unlink(saddr.sun_path);
295 conf_verify_dir();
296 if (bind(remote->fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
297 close(remote->fd);
298 remote->fd = -1;
299 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM,
300 "bind: %s", g_strerror(errno));
301 return FALSE;
304 if (listen(remote->fd, 1) < 0) {
305 close(remote->fd);
306 remote->fd = -1;
307 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM,
308 "listen: %s", g_strerror(errno));
309 return FALSE;
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,
316 io_cb, remote);
317 g_io_channel_unref(channel);
319 return TRUE;
322 gboolean
323 logjam_remote_stop_listening(LogJamRemote *remote, GError **err) {
324 char buf[128];
326 if (remote->fd <= 0)
327 return TRUE;
329 close(remote->fd);
330 remote->fd = -1;
332 make_remote_path(buf, 128);
333 unlink(buf);
335 return TRUE;