Allow doldacond-shell to be used with remote daemons as well.
[doldaconnect.git] / clients / gui-shell / dsh.c
CommitLineData
34d45a15
FT
1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2007 Fredrik Tolf <fredrik@dolda2000.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <unistd.h>
23#include <string.h>
24#include <errno.h>
25#include <pwd.h>
26#include <locale.h>
27#include <libintl.h>
28#include <signal.h>
29#include <sys/wait.h>
30#include <gtk/gtk.h>
31#include <gdk/gdkkeysyms.h>
32#include <stdarg.h>
33#include <doldaconnect/uilib.h>
34#include <doldaconnect/uimisc.h>
35#include <doldaconnect/utils.h>
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#ifdef HAVE_NOTIFY
42#include <libnotify/notify.h>
43#endif
44
45#define _(text) gettext(text)
46
47struct trinfo {
48 int ostate;
49};
50
51void updatewrite(void);
52
71aeadfc 53int remote = 0;
34d45a15
FT
54GtkStatusIcon *tray;
55pid_t dpid = 0, dcpid = 0;
56int connected = 0;
57int dstat;
58int derrfd, derrtag;
59char *derrbuf = NULL;
60size_t derrbufsize = 0, derrbufdata = 0;
61int dcfd, dcfdrtag, dcfdwtag = -1;
62GdkPixbuf *dcicon;
63#ifdef HAVE_NOTIFY
64NotifyNotification *trnote = NULL;
65#endif
66
67#include "dsh-start.gtkh"
68#include "dsh-menu.gtkh"
69
70int running(char *pf)
71{
72 FILE *pfs;
73 char buf[1024];
74 int pid;
75
76 if((pfs = fopen(pf, "r")) == NULL) {
77 perror(pf);
78 return(0);
79 }
80 fgets(buf, sizeof(buf), pfs);
81 fclose(pfs);
82 if((pid = atoi(buf)) == 0)
83 return(0);
84 return(!kill(pid, 0));
85}
86
87void derrcb(gpointer data, gint source, GdkInputCondition cond)
88{
89 int ret = 0;
90
91 sizebuf2(derrbuf, derrbufdata + 1024, 1);
92 ret = read(derrfd, derrbuf + derrbufdata, derrbufsize - derrbufdata);
93 if(ret <= 0) {
94 if(ret < 0)
95 bprintf(derrbuf, "\ncould not read from daemon: %s\n", strerror(errno));
96 gdk_input_remove(derrtag);
97 close(derrfd);
98 derrfd = -1;
99 } else {
100 derrbufdata += ret;
101 }
102}
103
104int msgbox(int type, int buttons, char *format, ...)
105{
106 GtkWidget *swnd;
107 va_list args;
108 char *buf;
109 int resp;
110
111 va_start(args, format);
112 buf = vsprintf2(format, args);
113 va_end(args);
114 swnd = gtk_message_dialog_new(NULL, 0, type, buttons, "%s", buf);
115 resp = gtk_dialog_run(GTK_DIALOG(swnd));
116 gtk_widget_destroy(swnd);
117 free(buf);
118 return(resp);
119}
120
121void destroytr(struct dc_transfer *tr)
122{
123 struct trinfo *tri;
124
125 tri = tr->udata;
126 free(tri);
127}
128
129void inittr(struct dc_transfer *tr)
130{
131 struct trinfo *tri;
132
133 tr->udata = tri = memset(smalloc(sizeof(*tri)), 0, sizeof(*tri));
134 tr->destroycb = destroytr;
135 tri->ostate = tr->state;
136}
137
138#ifdef HAVE_NOTIFY
139void notify(NotifyNotification **n, char *cat, char *title, char *body, ...)
140{
141 va_list args;
142 char *bbuf;
143
144 va_start(args, body);
145 bbuf = vsprintf2(body, args);
146 va_end(args);
147 if(*n == NULL) {
148 *n = notify_notification_new_with_status_icon(title, bbuf, NULL, tray);
149 notify_notification_set_icon_from_pixbuf(*n, dcicon);
150 } else {
151 notify_notification_update(*n, title, bbuf, NULL);
152 }
153 notify_notification_show(*n, NULL);
154}
155#endif
156
157void trstatechange(struct dc_transfer *tr, int ostate)
158{
159 if(ostate == DC_TRNS_MAIN) {
160 if(tr->state == DC_TRNS_DONE) {
161#ifdef HAVE_NOTIFY
162 notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), tr->path, tr->peernick);
163#endif
164 } else {
165#ifdef HAVE_NOTIFY
166 notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), tr->path, tr->peernick);
167#endif
168 }
169 }
170}
171
172void updatetrinfo(void)
173{
174 struct dc_transfer *tr;
175 struct trinfo *tri;
176
177 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
178 if(tr->udata == NULL) {
179 inittr(tr);
180 } else {
181 tri = tr->udata;
182 if(tri->ostate != tr->state) {
183 trstatechange(tr, tri->ostate);
184 tri->ostate = tr->state;
185 }
186 }
187 }
188}
189
190void trlscb(int resp, void *data)
191{
192 updatetrinfo();
193}
194
195void logincb(int err, wchar_t *reason, void *data)
196{
197 if(err != DC_LOGIN_ERR_SUCCESS) {
198 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
199 exit(1);
200 }
201 gtk_status_icon_set_tooltip(tray, "Dolda Connect");
202 dc_queuecmd(NULL, NULL, L"notify", L"trans:act", L"on", L"trans:prog", L"on", NULL);
203 dc_gettrlistasync(trlscb, NULL);
204 connected = 1;
205 updatewrite();
206}
207
208void dcfdcb(gpointer data, gint source, GdkInputCondition cond)
209{
210 struct dc_response *resp;
211
212 if(((cond & GDK_INPUT_READ) && dc_handleread()) || ((cond & GDK_INPUT_WRITE) && dc_handlewrite())) {
213 if(errno == 0) {
214 gtk_main_quit();
215 } else {
216 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
217 exit(1);
218 }
219 return;
220 }
221 while((resp = dc_getresp()) != NULL) {
222 if(!wcscmp(resp->cmdname, L".connect")) {
223 if(resp->code != 201) {
224 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
225 exit(1);
226 } else if(dc_checkprotocol(resp, DC_LATEST)) {
227 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
228 exit(1);
229 } else {
230 gtk_status_icon_set_tooltip(tray, _("Authenticating..."));
231 dc_loginasync(NULL, 1, dc_convnone, logincb, NULL);
232 }
233 } else if(!wcscmp(resp->cmdname, L".notify")) {
234 dc_uimisc_handlenotify(resp);
235 updatetrinfo();
236 }
237 }
238 updatewrite();
239}
240
241void updatewrite(void)
242{
243 if(dcfd < 0)
244 return;
245 if(dc_wantwrite()) {
246 if(dcfdwtag == -1)
247 dcfdwtag = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcb, NULL);
248 } else {
249 if(dcfdwtag != -1) {
250 gdk_input_remove(dcfdwtag);
251 dcfdwtag = -1;
252 }
253 }
254}
255
256void connectdc(void)
257{
71aeadfc 258 if((dcfd = dc_connect(remote?NULL:dc_srv_local)) < 0) {
34d45a15
FT
259 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
260 exit(1);
261 }
262 dcfdrtag = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcb, NULL);
263 updatewrite();
264 gtk_status_icon_set_tooltip(tray, _("Connecting to server..."));
265}
266
267void startdaemon(void)
268{
269 char pf[1024];
270 int pfd[2], i;
271
272 if(getenv("HOME") != NULL)
273 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getenv("HOME"));
274 else
275 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getpwuid(getuid())->pw_dir);
276 if(access(pf, F_OK) || !running(pf)) {
277 pipe(pfd);
278 if((dpid = fork()) == 0) {
279 dup2(pfd[1], 2);
280 for(i = 3; i < FD_SETSIZE; i++)
281 close(i);
282 execlp("doldacond", "doldacond", "-p", pf, NULL);
283 perror("doldacond");
284 exit(127);
285 }
286 if(dpid == -1)
287 abort();
288 close(pfd[1]);
289 derrfd = pfd[0];
290 derrtag = gdk_input_add(derrfd, GDK_INPUT_READ, derrcb, NULL);
291 create_start_wnd();
292 gtk_widget_show(start_wnd);
293 gtk_status_icon_set_tooltip(tray, _("Starting..."));
294 } else {
295 connectdc();
296 }
297}
298
299gboolean daemonized(gpointer uu)
300{
301 gtk_widget_hide(start_wnd);
302 dpid = 0;
303 if(derrfd != -1) {
304 gdk_input_remove(derrtag);
305 close(derrfd);
306 }
307 if(dstat != 0) {
308 gtk_status_icon_set_visible(tray, FALSE);
309 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(start_log)), derrbuf, derrbufdata);
310 gtk_widget_show(start_errwnd);
311 } else {
312 connectdc();
313 }
314 return(FALSE);
315}
316
317void sighandler(int sig)
318{
319 pid_t p;
320 int status;
321
322 if(sig == SIGCHLD) {
323 while((p = waitpid(-1, &status, WNOHANG)) > 0) {
324 if(p == dpid) {
325 dstat = status;
326 gtk_timeout_add(1, daemonized, NULL);
327 } else if(p == dcpid) {
328 dcpid = 0;
329 }
330 }
331 }
332}
333
334void dolcon(void)
335{
336 int i;
337
338 if((dcpid = fork()) == 0) {
339 for(i = 3; i < FD_SETSIZE; i++)
340 close(i);
71aeadfc
FT
341 if(remote)
342 execlp("dolcon", "dolcon", NULL);
343 else
344 execlp("dolcon", "dolcon", "-l", NULL);
34d45a15
FT
345 perror("dolcon");
346 exit(127);
347 }
348}
349
350void cb_shm_dolcon_activate(GtkWidget *uu1, gpointer uu2)
351{
352 dolcon();
353}
354
355void cb_shm_quit_activate(GtkWidget *uu1, gpointer uu2)
356{
357 dc_queuecmd(NULL, NULL, L"shutdown", NULL);
358 updatewrite();
359}
360
361void tray_activate(GtkStatusIcon *uu1, gpointer uu2)
362{
363 if(dpid != 0) {
364 gtk_widget_show(start_wnd);
365 } else if(connected) {
366 dolcon();
367 }
368}
369
370void tray_popup(GtkStatusIcon *uu1, guint button, guint time, gpointer uu2)
371{
372 gtk_menu_popup(GTK_MENU(shm_menu), NULL, NULL, NULL, NULL, button, time);
373}
374
375void cb_start_hide_clicked(GtkWidget *uu1, gpointer uu2)
376{
377 gtk_widget_hide(start_wnd);
378}
379
380void cb_start_abort_clicked(GtkWidget *uu1, gpointer uu2)
381{
382 kill(dpid, SIGINT);
383 exit(0);
384}
385
386void cb_start_errok_clicked(GtkWidget *uu1, gpointer uu2)
387{
388 gtk_main_quit();
389}
390
391#include "../dolda-icon.xpm"
392
393void inittray(void)
394{
395 tray = gtk_status_icon_new_from_pixbuf(gdk_pixbuf_scale_simple(dcicon, 24, 24, GDK_INTERP_BILINEAR));
396 gtk_status_icon_set_tooltip(tray, "");
397 g_signal_connect(G_OBJECT(tray), "activate", G_CALLBACK(tray_activate), (gpointer)NULL);
398 g_signal_connect(G_OBJECT(tray), "popup-menu", G_CALLBACK(tray_popup), (gpointer)NULL);
399}
400
401int main(int argc, char **argv)
402{
71aeadfc
FT
403 int c;
404
34d45a15
FT
405 setlocale(LC_ALL, "");
406 bindtextdomain(PACKAGE, LOCALEDIR);
407 textdomain(PACKAGE);
408 signal(SIGCHLD, sighandler);
409 dc_init();
410 gtk_init(&argc, &argv);
411#ifdef HAVE_NOTIFY
412 notify_init("Dolda Connect");
413#endif
71aeadfc
FT
414 while((c = getopt(argc, argv, "rh")) != -1) {
415 switch(c) {
416 case 'r':
417 remote = 1;
418 break;
419 case 'h':
420 printf("usage: doldacond-shell [-hr]\n");
421 printf("\t-h\tDisplay this help message\n");
422 printf("\t-r\tConnect to a remote host\n");
423 exit(0);
424 default:
425 fprintf(stderr, "usage: doldacond-shell [-hr]\n");
426 exit(1);
427 }
428 }
34d45a15
FT
429
430 create_shm_wnd();
431 dcicon = gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm);
432 gtk_window_set_default_icon(dcicon);
433 inittray();
71aeadfc
FT
434 if(remote)
435 connectdc();
436 else
437 startdaemon();
34d45a15
FT
438
439 gtk_main();
440
441 return(0);
442}
443
444#include "dsh-start.gtk"
445#include "dsh-menu.gtk"