Fixed buffer eating bug in transfer system.
[doldaconnect.git] / clients / tty / dcsh.c
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 <wchar.h>
24 #include <sys/poll.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <locale.h>
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 #include <doldaconnect/uilib.h>
33 #include <doldaconnect/uimisc.h>
34 #include <doldaconnect/utils.h>
35
36 int interactive = 0;
37 int verbose = 0;
38 int dcfd;
39
40 struct cmd {
41     wchar_t *name;
42     int (*handler)(int argc, wchar_t **argv);
43 };
44
45 void wcslimit(wchar_t *wcs, int limit) {
46     int i;
47     
48     if(wcslen(wcs) > limit) {
49         for(i = limit - 3; i < limit; i++)
50             wcs[i] = L'.';
51         wcs[i] = L'\0';
52     }
53 }
54
55 void wcslimitr(wchar_t *wcs, int limit) {
56     int i;
57     
58     if((i = wcslen(wcs)) > limit) {
59         memmove(wcs + 3, wcs + (i - limit + 3), sizeof(wchar_t) * (limit - 2));
60         for(i = 0; i < 3; i++)
61             wcs[i] = L'.';
62     }
63 }
64
65 int cmd_lsnodes(int argc, wchar_t **argv)
66 {
67     struct dc_response *resp;
68     struct dc_intresp *ires;
69     
70     resp = dc_gettaggedrespsync(dc_queuecmd(NULL, NULL, L"lsnodes", NULL));
71     if(resp->code == 200) {
72         if(interactive) {
73             printf("ID    NET  USERS  S NAME\n");
74             printf("----- ---- ------ - ----------------------------------------------------------\n");
75         }
76         while((ires = dc_interpret(resp)) != NULL) {
77             if(interactive)
78                 wcslimit(ires->argv[2].val.str, 58);
79             printf("%-5i %-4ls %-6i %c %ls\n", ires->argv[0].val.num, ires->argv[1].val.str, ires->argv[3].val.num, "SHED"[ires->argv[4].val.num], ires->argv[2].val.str);
80             dc_freeires(ires);
81         }
82     } else if(resp->code == 201) {
83     } else {
84         fprintf(stderr, "dcsh: %ls\n", resp->rlines[0].argv[0]);
85         return(1);
86     }
87     return(0);
88 }
89
90 int cmd_lsdl(int argc, wchar_t **argv)
91 {
92     struct dc_response *resp;
93     struct dc_intresp *ires;
94     
95     resp = dc_gettaggedrespsync(dc_queuecmd(NULL, NULL, L"lstrans", NULL));
96     if(resp->code == 200) {
97         if(interactive) {
98             printf("ID      S USER            FILE\n");
99             printf("------- - --------------- ----------------------------------------------------\n");
100         }
101         while((ires = dc_interpret(resp)) != NULL) {
102             if(ires->argv[1].val.num == DC_TRNSD_DOWN) {
103                 if(interactive) {
104                     wcslimit(ires->argv[4].val.str, 15);
105                     wcslimitr(ires->argv[5].val.str, 52);
106                 }
107                 printf("%-7i %c %-15ls %ls\n", ires->argv[0].val.num, "SHED"[ires->argv[2].val.num], ires->argv[4].val.str, ires->argv[5].val.str);
108             }
109             dc_freeires(ires);
110         }
111     } else if(resp->code == 201) {
112     } else {
113         fprintf(stderr, "dcsh: %ls\n", resp->rlines[0].argv[0]);
114         return(1);
115     }
116     return(0);
117 }
118
119 struct cmd commands[] = {
120     {L"hubs", cmd_lsnodes},
121     {L"lsdl", cmd_lsdl},
122     {NULL, NULL}
123 };
124
125 int run1(int argc, wchar_t **argv)
126 {
127     struct cmd *c;
128     
129     for(c = commands; c->handler != NULL; c++) {
130         if(!wcscmp(argv[0], c->name))
131             return(c->handler(argc, argv));
132     }
133     fprintf(stderr, "dcsh: no such command\n");
134     return(1);
135 }
136
137 int runchar(int argc, char **argv) {
138     int i, rv;
139     wchar_t **wargv, *buf;
140     size_t wargvsize, wargvdata;
141     
142     wargv = NULL;
143     wargvsize = wargvdata = 0;
144     for(i = 0; i < argc; i++) {
145         if((buf = icmbstowcs(argv[i], NULL)) == NULL) {
146             fprintf(stderr, "dcsh: could not convert %s to wide char: %s\n", argv[i], strerror(errno));
147             for(i = 0; i < wargvdata; i++)
148                 free(wargv[i]);
149             if(wargv != NULL)
150                 free(wargv);
151             return(1);
152         }
153         addtobuf(wargv, buf);
154     }
155     addtobuf(wargv, NULL);
156     rv = run1(wargvdata - 1, wargv);
157     dc_freewcsarr(wargv);
158     return(rv);
159 }
160
161 int shell(void)
162 {
163     int ret, argc;
164     wchar_t *buf, **argv;
165     char *p, cmdbuf[128];
166     int cmddata;
167     struct pollfd pfd[2];
168
169     if(interactive) {
170         fprintf(stderr, "dcsh> ");
171         fflush(stderr);
172     }
173     cmddata = 0;
174     while(1) {
175         pfd[0].fd = dcfd;
176         pfd[0].events = POLLIN;
177         if(dc_wantwrite())
178             pfd[0].events |= POLLOUT;
179         pfd[1].fd = 0;
180         pfd[1].events = POLLIN;
181         pfd[1].revents = 0;
182         if(poll(pfd, 2, -1) < 0) {
183             if(errno != EINTR) {
184                 perror("dcsh: poll");
185                 exit(1);
186             }
187         }
188         if(((pfd[0].revents & POLLIN) && dc_handleread()) || ((pfd[0].revents & POLLOUT) && dc_handlewrite()))
189             return(1);
190         if(pfd[1].revents) {
191             ret = read(0, cmdbuf + cmddata, sizeof(cmdbuf) - cmddata);
192             if(ret < 0) {
193                 fprintf(stderr, "dcsh: stdin: %s\n", strerror(errno));
194                 return(1);
195             } else if(ret == 0) {
196                 if(interactive)
197                     fprintf(stderr, "\n");
198                 return(0);
199             }
200             cmddata += ret;
201             while((p = memchr(cmdbuf, '\n', cmddata)) != NULL) {
202                 *(p++) = 0;
203                 if((buf = icmbstowcs(cmdbuf, NULL)) == NULL) {
204                     fprintf(stderr, "dcsh: could not convert command to wide chars: %s\n", strerror(errno));
205                 } else {
206                     argv = dc_lexsexpr(buf);
207                     free(buf);
208                     for(argc = 0; argv[argc] != NULL; argc++);
209                     if(argc > 0)
210                         run1(argc, argv);
211                     dc_freewcsarr(argv);
212                 }
213                 memmove(cmdbuf, p, cmddata -= (p - cmdbuf));
214                 if(interactive) {
215                     fprintf(stderr, "dcsh> ");
216                     fflush(stderr);
217                 }
218             }
219         }
220     }
221 }
222
223 int main(int argc, char **argv)
224 {
225     int c, rv;
226     char *server;
227     char *username;
228     int authless, authia;
229     
230     setlocale(LC_ALL, "");
231     server = username = NULL;
232     authless = authia = 1;
233     while((c = getopt(argc, argv, "hvIAs:u:")) != -1) {
234         switch(c) {
235         case 's':
236             server = optarg;
237             break;
238         case 'u':
239             username = optarg;
240             break;
241         case 'I':
242             authia = 0;
243             break;
244         case 'A':
245             authless = 0;
246             break;
247         case 'v':
248             verbose++;
249             break;
250         default:
251             fprintf(stderr, "usage: dcsh [-AIhv] [-s SERVER] [-u USERNAME] [COMMAND [ARGS...]]\n");
252             exit((c == 'h')?0:1);
253         }
254     }
255     dc_init();
256     
257     if(verbose)
258         fprintf(stderr, "connecting...\n");
259     if((dcfd = dc_connectsync2(server, 1)) < 0) {
260         perror("dcsh: could not connect to server");
261         exit(1);
262     }
263     if(verbose)
264         fprintf(stderr, "authenticating...\n");
265     if(dc_login(username, authless, authia?dc_convtty:dc_convnone, NULL) != DC_LOGIN_ERR_SUCCESS) {
266         fprintf(stderr, "dcsh: authentication unsuccessful\n");
267         exit(1);
268     }
269     if(verbose)
270         fprintf(stderr, "done\n");
271     if(optind < argc) {
272         interactive = isatty(1);
273         rv = runchar(argc - optind, argv + optind);
274     } else {
275         interactive = isatty(0);
276         rv = shell();
277     }
278     return(rv);
279 }