Allow connecting to any server in dsh.
[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;
0d12617c 49 int opos, spos, speed;
fa9a8ac1 50 time_t lastprog;
0d12617c 51 double sprog;
34d45a15
FT
52};
53
54void updatewrite(void);
55
71aeadfc 56int remote = 0;
a0ca560e 57char *server;
34d45a15
FT
58GtkStatusIcon *tray;
59pid_t dpid = 0, dcpid = 0;
60int connected = 0;
61int dstat;
62int derrfd, derrtag;
63char *derrbuf = NULL;
64size_t derrbufsize = 0, derrbufdata = 0;
65int dcfd, dcfdrtag, dcfdwtag = -1;
66GdkPixbuf *dcicon;
67#ifdef HAVE_NOTIFY
68NotifyNotification *trnote = NULL;
69#endif
70
71#include "dsh-start.gtkh"
72#include "dsh-menu.gtkh"
73
74int running(char *pf)
75{
76 FILE *pfs;
77 char buf[1024];
78 int pid;
79
80 if((pfs = fopen(pf, "r")) == NULL) {
81 perror(pf);
82 return(0);
83 }
84 fgets(buf, sizeof(buf), pfs);
85 fclose(pfs);
86 if((pid = atoi(buf)) == 0)
87 return(0);
88 return(!kill(pid, 0));
89}
90
91void derrcb(gpointer data, gint source, GdkInputCondition cond)
92{
93 int ret = 0;
94
95 sizebuf2(derrbuf, derrbufdata + 1024, 1);
96 ret = read(derrfd, derrbuf + derrbufdata, derrbufsize - derrbufdata);
97 if(ret <= 0) {
98 if(ret < 0)
99 bprintf(derrbuf, "\ncould not read from daemon: %s\n", strerror(errno));
100 gdk_input_remove(derrtag);
101 close(derrfd);
102 derrfd = -1;
103 } else {
104 derrbufdata += ret;
105 }
106}
107
108int msgbox(int type, int buttons, char *format, ...)
109{
110 GtkWidget *swnd;
111 va_list args;
112 char *buf;
113 int resp;
114
115 va_start(args, format);
116 buf = vsprintf2(format, args);
117 va_end(args);
118 swnd = gtk_message_dialog_new(NULL, 0, type, buttons, "%s", buf);
119 resp = gtk_dialog_run(GTK_DIALOG(swnd));
120 gtk_widget_destroy(swnd);
121 free(buf);
122 return(resp);
123}
124
125void destroytr(struct dc_transfer *tr)
126{
127 struct trinfo *tri;
128
129 tri = tr->udata;
130 free(tri);
131}
132
133void inittr(struct dc_transfer *tr)
134{
135 struct trinfo *tri;
136
137 tr->udata = tri = memset(smalloc(sizeof(*tri)), 0, sizeof(*tri));
138 tr->destroycb = destroytr;
139 tri->ostate = tr->state;
0d12617c
FT
140 tri->spos = tri->opos = tr->curpos;
141 tri->speed = -1;
fa9a8ac1 142 tri->lastprog = time(NULL);
0d12617c 143 tri->sprog = ntime();
34d45a15
FT
144}
145
146#ifdef HAVE_NOTIFY
147void notify(NotifyNotification **n, char *cat, char *title, char *body, ...)
148{
149 va_list args;
150 char *bbuf;
151
152 va_start(args, body);
153 bbuf = vsprintf2(body, args);
154 va_end(args);
155 if(*n == NULL) {
156 *n = notify_notification_new_with_status_icon(title, bbuf, NULL, tray);
157 notify_notification_set_icon_from_pixbuf(*n, dcicon);
158 } else {
159 notify_notification_update(*n, title, bbuf, NULL);
160 }
161 notify_notification_show(*n, NULL);
162}
163#endif
164
fa9a8ac1
FT
165/* XXX: Achtung! Too DC-specific! */
166wchar_t *getfilename(wchar_t *path)
167{
168 wchar_t *p;
169
170 if((p = wcsrchr(path, L'\\')) == NULL)
171 return(path);
172 else
173 return(p + 1);
174}
175
0d12617c
FT
176char *bytes2si(long long bytes)
177{
178 int i;
179 double b;
180 char *sd;
181 static char ret[64];
182
183 b = bytes;
184 for(i = 0; (b >= 1024) && (i < 4); i++)
185 b /= 1024;
186 if(i == 0)
187 sd = "B";
188 else if(i == 1)
189 sd = "kiB";
190 else if(i == 2)
191 sd = "MiB";
192 else if(i == 3)
193 sd = "GiB";
194 else
195 sd = "TiB";
196 snprintf(ret, 64, "%.1f %s", b, sd);
197 return(ret);
198}
199
200void updatetooltip(void)
201{
202 struct dc_transfer *tr;
203 struct trinfo *tri;
0c095756 204 int t, i, a, st, bc, bt;
0d12617c
FT
205 char *buf;
206 size_t bufsize, bufdata;
207
208 t = i = a = 0;
0c095756 209 st = bc = bt = -1;
0d12617c
FT
210 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
211 if(tr->dir != DC_TRNSD_DOWN)
212 continue;
213 tri = tr->udata;
214 t++;
215 if(tr->state == DC_TRNS_WAITING)
216 i++;
217 else if((tr->state == DC_TRNS_HS) || (tr->state == DC_TRNS_MAIN))
218 a++;
0c095756
FT
219 if((tr->state == DC_TRNS_MAIN)) {
220 if(bt == -1)
221 bc = bt = 0;
222 bc += tr->curpos;
223 bt += tr->size;
224 if(tri->speed != -1) {
225 if(st == -1)
226 st = 0;
227 st += tri->speed;
228 }
0d12617c
FT
229 }
230 }
231 buf = NULL;
232 bufsize = bufdata = 0;
f782eb97 233 bprintf(buf, "%s: %i", _("Transfers"), t);
0d12617c
FT
234 if(t > 0)
235 bprintf(buf, " (%i/%i)", i, a);
0c095756 236 if(bt > 0)
2c16e7a5 237 bprintf(buf, ", %.1f%%", ((double)bc / (double)bt) * 100.0);
0c095756 238 if(st != -1)
0d12617c 239 bprintf(buf, ", %s/s", bytes2si(st));
0d12617c
FT
240 addtobuf(buf, 0);
241 gtk_status_icon_set_tooltip(tray, buf);
242 free(buf);
243}
244
34d45a15
FT
245void trstatechange(struct dc_transfer *tr, int ostate)
246{
0d12617c
FT
247 struct trinfo *tri;
248
249 tri = tr->udata;
fa9a8ac1 250 if((ostate == DC_TRNS_MAIN) && (tr->dir == DC_TRNSD_DOWN)) {
34d45a15
FT
251 if(tr->state == DC_TRNS_DONE) {
252#ifdef HAVE_NOTIFY
43edea91
FT
253 if(dcpid == 0)
254 notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), getfilename(tr->path), tr->peernick);
34d45a15
FT
255#endif
256 } else {
257#ifdef HAVE_NOTIFY
43edea91
FT
258 if(dcpid == 0)
259 notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), getfilename(tr->path), tr->peernick);
34d45a15
FT
260#endif
261 }
262 }
0d12617c
FT
263 if(tr->state == DC_TRNS_MAIN) {
264 tri->speed = -1;
265 tri->spos = tr->curpos;
266 tri->sprog = ntime();
267 }
34d45a15
FT
268}
269
270void updatetrinfo(void)
271{
272 struct dc_transfer *tr;
273 struct trinfo *tri;
fa9a8ac1 274 time_t now;
0d12617c 275 double dnow;
34d45a15 276
fa9a8ac1 277 now = time(NULL);
0d12617c 278 dnow = ntime();
34d45a15
FT
279 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
280 if(tr->udata == NULL) {
281 inittr(tr);
282 } else {
283 tri = tr->udata;
284 if(tri->ostate != tr->state) {
285 trstatechange(tr, tri->ostate);
286 tri->ostate = tr->state;
287 }
fa9a8ac1
FT
288 if(tri->opos != tr->curpos) {
289 tri->opos = tr->curpos;
290 tri->lastprog = now;
291 }
292#ifdef NOTIFY
293 if((tr->state = DC_TRNS_MAIN) && (now - tri->lastprog > 600)) {
43edea91
FT
294 if(dcpid == 0)
295 notify(&trnote, "transfer.error", _("Transfer stalled"), _("The transfer of %ls from %ls has not made progress for 10 minutes"), getfilename(tr->path), tr->peernick);
fa9a8ac1
FT
296 }
297#endif
0d12617c
FT
298 if((tr->state == DC_TRNS_MAIN) && (dnow - tri->sprog > 10)) {
299 tri->speed = ((double)(tr->curpos - tri->spos) / (dnow - tri->sprog));
300 tri->spos = tr->curpos;
301 tri->sprog = dnow;
302 }
34d45a15
FT
303 }
304 }
0d12617c 305 updatetooltip();
34d45a15
FT
306}
307
308void trlscb(int resp, void *data)
309{
310 updatetrinfo();
311}
312
0d12617c
FT
313gint trupdatecb(gpointer data)
314{
315 updatetrinfo();
316 return(TRUE);
317}
318
34d45a15
FT
319void logincb(int err, wchar_t *reason, void *data)
320{
321 if(err != DC_LOGIN_ERR_SUCCESS) {
322 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
323 exit(1);
324 }
34d45a15
FT
325 dc_queuecmd(NULL, NULL, L"notify", L"trans:act", L"on", L"trans:prog", L"on", NULL);
326 dc_gettrlistasync(trlscb, NULL);
327 connected = 1;
328 updatewrite();
329}
330
331void dcfdcb(gpointer data, gint source, GdkInputCondition cond)
332{
333 struct dc_response *resp;
334
335 if(((cond & GDK_INPUT_READ) && dc_handleread()) || ((cond & GDK_INPUT_WRITE) && dc_handlewrite())) {
336 if(errno == 0) {
337 gtk_main_quit();
338 } else {
339 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
340 exit(1);
341 }
342 return;
343 }
344 while((resp = dc_getresp()) != NULL) {
345 if(!wcscmp(resp->cmdname, L".connect")) {
346 if(resp->code != 201) {
347 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
348 exit(1);
349 } else if(dc_checkprotocol(resp, DC_LATEST)) {
350 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
351 exit(1);
352 } else {
353 gtk_status_icon_set_tooltip(tray, _("Authenticating..."));
354 dc_loginasync(NULL, 1, dc_convnone, logincb, NULL);
355 }
356 } else if(!wcscmp(resp->cmdname, L".notify")) {
357 dc_uimisc_handlenotify(resp);
358 updatetrinfo();
359 }
d20e3861 360 dc_freeresp(resp);
34d45a15
FT
361 }
362 updatewrite();
363}
364
365void updatewrite(void)
366{
367 if(dcfd < 0)
368 return;
369 if(dc_wantwrite()) {
370 if(dcfdwtag == -1)
371 dcfdwtag = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcb, NULL);
372 } else {
373 if(dcfdwtag != -1) {
374 gdk_input_remove(dcfdwtag);
375 dcfdwtag = -1;
376 }
377 }
378}
379
380void connectdc(void)
381{
a0ca560e 382 if((dcfd = dc_connect(server)) < 0) {
34d45a15
FT
383 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
384 exit(1);
385 }
386 dcfdrtag = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcb, NULL);
387 updatewrite();
388 gtk_status_icon_set_tooltip(tray, _("Connecting to server..."));
389}
390
391void startdaemon(void)
392{
393 char pf[1024];
394 int pfd[2], i;
395
396 if(getenv("HOME") != NULL)
397 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getenv("HOME"));
398 else
399 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getpwuid(getuid())->pw_dir);
400 if(access(pf, F_OK) || !running(pf)) {
401 pipe(pfd);
402 if((dpid = fork()) == 0) {
403 dup2(pfd[1], 2);
404 for(i = 3; i < FD_SETSIZE; i++)
405 close(i);
406 execlp("doldacond", "doldacond", "-p", pf, NULL);
407 perror("doldacond");
408 exit(127);
409 }
410 if(dpid == -1)
411 abort();
412 close(pfd[1]);
413 derrfd = pfd[0];
414 derrtag = gdk_input_add(derrfd, GDK_INPUT_READ, derrcb, NULL);
415 create_start_wnd();
416 gtk_widget_show(start_wnd);
417 gtk_status_icon_set_tooltip(tray, _("Starting..."));
418 } else {
419 connectdc();
420 }
421}
422
423gboolean daemonized(gpointer uu)
424{
425 gtk_widget_hide(start_wnd);
426 dpid = 0;
427 if(derrfd != -1) {
428 gdk_input_remove(derrtag);
429 close(derrfd);
430 }
431 if(dstat != 0) {
432 gtk_status_icon_set_visible(tray, FALSE);
433 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(start_log)), derrbuf, derrbufdata);
434 gtk_widget_show(start_errwnd);
435 } else {
436 connectdc();
437 }
438 return(FALSE);
439}
440
441void sighandler(int sig)
442{
443 pid_t p;
444 int status;
445
446 if(sig == SIGCHLD) {
447 while((p = waitpid(-1, &status, WNOHANG)) > 0) {
448 if(p == dpid) {
449 dstat = status;
450 gtk_timeout_add(1, daemonized, NULL);
451 } else if(p == dcpid) {
452 dcpid = 0;
453 }
454 }
455 }
456}
457
458void dolcon(void)
459{
460 int i;
461
462 if((dcpid = fork()) == 0) {
463 for(i = 3; i < FD_SETSIZE; i++)
464 close(i);
71aeadfc
FT
465 if(remote)
466 execlp("dolcon", "dolcon", NULL);
467 else
468 execlp("dolcon", "dolcon", "-l", NULL);
34d45a15
FT
469 perror("dolcon");
470 exit(127);
471 }
472}
473
31a01fdd
FT
474void cb_shm_dolconf_activate(GtkWidget *uu1, gpointer uu2)
475{
476 int i;
477
478 if((dcpid = fork()) == 0) {
479 for(i = 3; i < FD_SETSIZE; i++)
480 close(i);
481 execlp("dolconf", "dolconf", NULL);
482 perror("dolconf");
483 exit(127);
484 }
485}
486
34d45a15
FT
487void cb_shm_dolcon_activate(GtkWidget *uu1, gpointer uu2)
488{
489 dolcon();
490}
491
492void cb_shm_quit_activate(GtkWidget *uu1, gpointer uu2)
493{
494 dc_queuecmd(NULL, NULL, L"shutdown", NULL);
495 updatewrite();
496}
497
498void tray_activate(GtkStatusIcon *uu1, gpointer uu2)
499{
500 if(dpid != 0) {
501 gtk_widget_show(start_wnd);
502 } else if(connected) {
503 dolcon();
504 }
505}
506
507void tray_popup(GtkStatusIcon *uu1, guint button, guint time, gpointer uu2)
508{
509 gtk_menu_popup(GTK_MENU(shm_menu), NULL, NULL, NULL, NULL, button, time);
510}
511
512void cb_start_hide_clicked(GtkWidget *uu1, gpointer uu2)
513{
514 gtk_widget_hide(start_wnd);
515}
516
517void cb_start_abort_clicked(GtkWidget *uu1, gpointer uu2)
518{
519 kill(dpid, SIGINT);
520 exit(0);
521}
522
523void cb_start_errok_clicked(GtkWidget *uu1, gpointer uu2)
524{
525 gtk_main_quit();
526}
527
528#include "../dolda-icon.xpm"
529
530void inittray(void)
531{
532 tray = gtk_status_icon_new_from_pixbuf(gdk_pixbuf_scale_simple(dcicon, 24, 24, GDK_INTERP_BILINEAR));
533 gtk_status_icon_set_tooltip(tray, "");
534 g_signal_connect(G_OBJECT(tray), "activate", G_CALLBACK(tray_activate), (gpointer)NULL);
535 g_signal_connect(G_OBJECT(tray), "popup-menu", G_CALLBACK(tray_popup), (gpointer)NULL);
536}
537
538int main(int argc, char **argv)
539{
71aeadfc
FT
540 int c;
541
34d45a15
FT
542 setlocale(LC_ALL, "");
543 bindtextdomain(PACKAGE, LOCALEDIR);
544 textdomain(PACKAGE);
545 signal(SIGCHLD, sighandler);
546 dc_init();
a0ca560e 547 server = dc_srv_local;
34d45a15
FT
548 gtk_init(&argc, &argv);
549#ifdef HAVE_NOTIFY
550 notify_init("Dolda Connect");
551#endif
a0ca560e 552 while((c = getopt(argc, argv, "rhs:")) != -1) {
71aeadfc
FT
553 switch(c) {
554 case 'r':
555 remote = 1;
a0ca560e
FT
556 server = NULL;
557 break;
558 case 's':
559 remote = 1;
560 server = optarg;
71aeadfc
FT
561 break;
562 case 'h':
563 printf("usage: doldacond-shell [-hr]\n");
564 printf("\t-h\tDisplay this help message\n");
565 printf("\t-r\tConnect to a remote host\n");
566 exit(0);
567 default:
568 fprintf(stderr, "usage: doldacond-shell [-hr]\n");
569 exit(1);
570 }
571 }
34d45a15
FT
572
573 create_shm_wnd();
574 dcicon = gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm);
575 gtk_window_set_default_icon(dcicon);
576 inittray();
71aeadfc
FT
577 if(remote)
578 connectdc();
579 else
580 startdaemon();
34d45a15 581
0d12617c 582 g_timeout_add(10000, trupdatecb, NULL);
34d45a15
FT
583 gtk_main();
584
585 return(0);
586}
587
588#include "dsh-start.gtk"
589#include "dsh-menu.gtk"