source: libcaca/trunk/src/cacaserver.c @ 1902

Last change on this file since 1902 was 1770, checked in by Sam Hocevar, 13 years ago
  • Property svn:keywords set to Id
File size: 14.7 KB
Line 
1/*
2 *  cacaserver    Colour ASCII-Art library
3 *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
4 *                2006 Sam Hocevar <sam@zoy.org>
5 *                All Rights Reserved
6 *
7 *  $Id: cacaserver.c 1770 2007-06-27 13:03:31Z sam $
8 *
9 *  This program is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16#include "config.h"
17#include "common.h"
18
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#if defined(HAVE_ARPA_INET_H)
23#   include <arpa/inet.h>
24#   if defined(USE_COCOA) && defined(HAVE_NETINET_IN_H)
25/* for sockaddr_in (not included from arpa/inet.h in 10.3.9 SDK) */
26#       include <netinet/in.h>
27#   endif
28#elif defined(HAVE_NETINET_IN_H)
29#   include <netinet/in.h>
30#elif defined(HAVE_WINSOCK2_H)
31#   include <winsock2.h>
32#   include <ws2tcpip.h>
33#   define USE_WINSOCK 1
34#endif
35#if defined(HAVE_UNISTD_H)
36#   include <unistd.h>
37#endif
38#include <sys/types.h>
39#include <sys/socket.h>
40#include <fcntl.h>
41#include <signal.h>
42#include <errno.h>
43#include <stdarg.h>
44
45#ifndef USE_WINSOCK
46#   define USE_WINSOCK 0
47#endif
48
49#include "cucul.h"
50
51#define BACKLOG 1337     /* Number of pending connections */
52#define INBUFFER 32      /* Size of per-client input buffer */
53#define OUTBUFFER 300000 /* Size of per-client output buffer */
54
55/* Following vars are static */
56#define INIT_PREFIX \
57    "\xff\xfb\x01"     /* WILL ECHO */ \
58    "\xff\xfb\x03"     /* WILL SUPPRESS GO AHEAD */ \
59    "\xff\xfd\x31"     /* DO NAWS */ \
60    "\xff\x1f\xfa____" /* SB NAWS */ \
61    "\xff\xf0"         /* SE */  \
62    "\x1b]2;caca for the network\x07" /* Change window title */ \
63    "\x1b[H\x1b[J" /* Clear screen */
64    /*"\x1b[?25l"*/ /* Hide cursor */
65
66#define ANSI_PREFIX \
67    "\x1b[1;1H" /* move(0,0) */ \
68    "\x1b[1;1H" /* move(0,0) again */
69
70#define ANSI_RESET \
71    "    " /* Garbage */ \
72    "\x1b[?1049h" /* Clear screen */ \
73    "\x1b[?1049h" /* Clear screen again */
74
75static char const telnet_commands[16][5] =
76{
77    "SE  ", "NOP ", "DM  ", "BRK ", "IP  ", "AO  ", "AYT ", "EC  ",
78    "EL  ", "GA  ", "SB  ", "WILL", "WONT", "DO  ", "DONT", "IAC "
79};
80
81static char const telnet_options[37][5] =
82{
83    "????", "ECHO", "????", "SUGH", "????", "STTS", "TIMK", "????",
84    "????", "????", "????", "????", "????", "????", "????", "????",
85    "????", "????", "????", "????", "????", "????", "????", "????",
86    "TTYP", "????", "????", "????", "????", "????", "????", "NAWS",
87    "TRSP", "RMFC", "LIMO", "????", "EVAR"
88};
89
90#define COMMAND_NAME(x) (x>=240)?telnet_commands[x-240]:"????"
91#define OPTION_NAME(x) (x<=36)?telnet_options[x]:"????"
92
93struct client
94{
95    int fd;
96    int ready;
97    uint8_t inbuf[INBUFFER];
98    int inbytes;
99    uint8_t outbuf[OUTBUFFER];
100    int start, stop;
101};
102
103struct server
104{
105    unsigned int width, height;
106    unsigned int port;
107    int sockfd;
108    struct sockaddr_in my_addr;
109    socklen_t sin_size;
110
111    /* Input buffer */
112    uint8_t *input;
113    unsigned int read;
114
115    char prefix[sizeof(INIT_PREFIX)];
116
117    cucul_canvas_t *canvas;
118    void *buffer;
119    unsigned long int buflen;
120
121    int client_count;
122    struct client *clients;
123
124    RETSIGTYPE (*sigpipe_handler)(int);
125};
126
127static void manage_connections(struct server *server);
128static int send_data(struct server *server, struct client *c);
129ssize_t nonblock_write(int fd, void *buf, size_t len);
130
131int main(void)
132{
133    int i, yes = 1, flags;
134    struct server *server;
135    char *tmp;
136
137#if USE_WINSOCK
138    WORD winsockVersion;
139    WSADATA wsaData;
140    winsockVersion = MAKEWORD(1, 1);
141
142    WSAStartup(winsockVersion, &wsaData);
143#endif
144    server = malloc(sizeof(struct server));
145
146    server->input = malloc(12);
147    server->read = 0;
148
149    server->client_count = 0;
150    server->clients = NULL;
151    server->port = 0xCACA; /* 51914 */
152
153    /* FIXME, handle >255 sizes */
154    memcpy(server->prefix, INIT_PREFIX, sizeof(INIT_PREFIX));
155    tmp = strstr(server->prefix, "____");
156    tmp[0] = (unsigned char) (server->width & 0xff00) >> 8;
157    tmp[1] = (unsigned char) server->width & 0xff;
158    tmp[2] = (unsigned char) (server->height & 0xff00) >> 8;
159    tmp[3] = (unsigned char) server->height & 0xff;
160
161    if((server->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
162    {
163        perror("socket");
164        return -1;
165    }
166
167    if(setsockopt(server->sockfd, SOL_SOCKET,
168                  SO_REUSEADDR, &yes, sizeof(int)) == -1)
169    {
170        perror("setsockopt SO_REUSEADDR");
171        return -1;
172    }
173
174    server->my_addr.sin_family = AF_INET;
175    server-> my_addr.sin_port = htons(server->port);
176    server->my_addr.sin_addr.s_addr = INADDR_ANY;
177    memset(&(server->my_addr.sin_zero), '\0', 8);
178
179    if(bind(server->sockfd, (struct sockaddr *)&server->my_addr,
180             sizeof(struct sockaddr)) == -1)
181    {
182        perror("bind");
183        return -1;
184    }
185
186    /* Non blocking socket */
187    flags = fcntl(server->sockfd, F_GETFL, 0);
188    fcntl(server->sockfd, F_SETFL, flags | O_NONBLOCK);
189
190    if(listen(server->sockfd, BACKLOG) == -1)
191    {
192        perror("listen");
193        return -1;
194    }
195
196    server->canvas = cucul_create_canvas(0, 0);
197    server->buffer = NULL;
198
199    /* Ignore SIGPIPE */
200    server->sigpipe_handler = signal(SIGPIPE, SIG_IGN);
201
202    fprintf(stderr, "initialised network, listening on port %i\n",
203                    server->port);
204
205    /* Main loop */
206    for(;;)
207    {
208restart:
209        /* Manage new connections as this function will be called sometimes
210         * more often than display */
211        manage_connections(server);
212
213        /* Read data from stdin */
214        if(server->read < 12)
215        {
216            read(0, server->input + server->read, 12 - server->read);
217            server->read = 12;
218        }
219
220        while(cucul_import_memory(server->canvas, server->input,
221                                  server->read, "caca") < 0)
222        {
223            memmove(server->input, server->input + 1, server->read - 1);
224            read(0, server->input + server->read - 1, 1);
225        }
226
227        for(;;)
228        {
229            long int needed;
230            ssize_t wanted;
231
232            needed = cucul_import_memory(server->canvas, server->input,
233                                         server->read, "caca");
234            if(needed < 0)
235                goto restart;
236
237            if(needed > 0)
238            {
239                server->read -= needed;
240                memmove(server->input, server->input + needed, server->read);
241                break;
242            }
243
244            server->input = realloc(server->input, server->read + 128);
245            wanted = read(0, server->input + server->read, 128);
246            if(wanted < 0)
247                goto restart;
248            server->read += wanted;
249        }
250           
251        /* Free the previous export buffer, if any */
252        if(server->buffer)
253        {
254            free(server->buffer);
255            server->buffer = NULL;
256        }
257
258        /* Get ANSI representation of the image and skip the end-of buffer
259         * linefeed ("\r\n", 2 byte) */
260        server->buffer = cucul_export_memory(server->canvas, "utf8cr",
261                                             &server->buflen);
262        server->buflen -= 2;
263
264        for(i = 0; i < server->client_count; i++)
265        {
266            if(server->clients[i].fd == -1)
267                continue;
268
269            if(send_data(server, &server->clients[i]))
270            {
271                fprintf(stderr, "[%i] dropped connection\n",
272                                server->clients[i].fd);
273                close(server->clients[i].fd);
274                server->clients[i].fd = -1;
275            }
276        }
277    }
278
279    /* Kill all remaining clients */
280    for(i = 0; i < server->client_count; i++)
281    {
282        if(server->clients[i].fd == -1)
283            continue;
284
285        close(server->clients[i].fd);
286        server->clients[i].fd = -1;
287    }
288
289    if(server->buffer)
290        free(server->buffer);
291
292    cucul_free_canvas(server->canvas);
293
294    /* Restore SIGPIPE handler */
295    signal(SIGPIPE, server->sigpipe_handler);
296
297    free(server);
298
299#if USE_WINSOCK
300    WSACleanup();
301#endif
302    return 0;
303}
304
305/*
306 * XXX: The following functions are local
307 */
308
309static void manage_connections(struct server *server)
310{
311    int fd, flags;
312    struct sockaddr_in remote_addr;
313    socklen_t len = sizeof(struct sockaddr_in);
314
315    fd = accept(server->sockfd, (struct sockaddr *)&remote_addr, &len);
316    if(fd == -1)
317        return;
318
319    fprintf(stderr, "[%i] connected from %s\n",
320                    fd, inet_ntoa(remote_addr.sin_addr));
321
322    /* Non blocking socket */
323    flags = fcntl(fd, F_SETFL, 0);
324    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
325
326    if(server->clients == NULL)
327    {
328        server->clients = malloc(sizeof(struct client));
329        if(server->clients == NULL)
330            return;
331    }
332    else
333    {
334        server->clients = realloc(server->clients,
335                            (server->client_count+1) * sizeof(struct client));
336    }
337
338    server->clients[server->client_count].fd = fd;
339    server->clients[server->client_count].ready = 0;
340    server->clients[server->client_count].inbytes = 0;
341    server->clients[server->client_count].start = 0;
342    server->clients[server->client_count].stop = 0;
343
344    /* If we already have data to send, send it to the new client */
345    if(send_data(server, &server->clients[server->client_count]))
346    {
347        fprintf(stderr, "[%i] dropped connection\n", fd);
348        close(fd);
349        server->clients[server->client_count].fd = -1;
350        return;
351    }
352
353    server->client_count++;
354}
355
356static int send_data(struct server *server, struct client *c)
357{
358    ssize_t ret;
359
360    /* Not for us */
361    if(c->fd < 0)
362        return -1;
363
364    /* Listen to incoming data */
365    for(;;)
366    {
367        ret = read(c->fd, c->inbuf + c->inbytes, 1);
368        if(ret <= 0)
369            break;
370
371        c->inbytes++;
372
373        /* Check for telnet sequences */
374        if(c->inbuf[0] == 0xff)
375        {
376            if(c->inbytes == 1)
377            {
378                ;
379            }
380            else if(c->inbuf[1] == 0xfd || c->inbuf[1] == 0xfc)
381            {
382                if(c->inbytes == 3)
383                {
384                    fprintf(stderr, "[%i] said: %.02x %.02x %.02x (%s %s %s)\n",
385                            c->fd, c->inbuf[0], c->inbuf[1], c->inbuf[2],
386                            COMMAND_NAME(c->inbuf[0]), COMMAND_NAME(c->inbuf[1]), OPTION_NAME(c->inbuf[2]));
387                    /* Just ignore, lol */
388                    c->inbytes = 0;
389                }
390            }
391            else
392                c->inbytes = 0;
393        }
394        else if(c->inbytes == 1)
395        {
396            if(c->inbuf[0] == 0x03)
397            {
398                fprintf(stderr, "[%i] pressed C-c\n", c->fd);
399                return -1; /* User requested to quit */
400            }
401
402            c->inbytes = 0;
403        }
404    }
405
406    /* Send the telnet initialisation commands */
407    if(!c->ready)
408    {
409        ret = nonblock_write(c->fd, server->prefix, sizeof(INIT_PREFIX));
410        if(ret == -1)
411            return (errno == EAGAIN) ? 0 : -1;
412
413        if(ret < (ssize_t)sizeof(INIT_PREFIX))
414            return 0;
415
416        c->ready = 1;
417    }
418
419    /* No error, there's just nothing to send yet */
420    if(!server->buffer)
421        return 0;
422
423    /* If we have backlog, send the backlog */
424    if(c->stop)
425    {
426        ret = nonblock_write(c->fd, c->outbuf + c->start, c->stop - c->start);
427
428        if(ret == -1)
429        {
430            if(errno == EAGAIN)
431                ret = 0;
432            else
433            {
434                fprintf(stderr, "[%i] failed (%s)\n",
435                        c->fd, strerror(errno));
436                return -1;
437            }
438        }
439
440        if(ret == c->stop - c->start)
441        {
442            /* We got rid of the backlog! */
443            c->start = c->stop = 0;
444        }
445        else
446        {
447            c->start += ret;
448
449            if(c->stop - c->start + strlen(ANSI_PREFIX) + server->buflen
450                > OUTBUFFER)
451            {
452                /* Overflow! Empty buffer and start again */
453                memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
454                c->start = 0;
455                c->stop = strlen(ANSI_RESET);
456                return 0;
457            }
458
459            /* Need to move? */
460            if(c->stop + strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER)
461            {
462                memmove(c->outbuf, c->outbuf + c->start, c->stop - c->start);
463                c->stop -= c->start;
464                c->start = 0;
465            }
466
467            memcpy(c->outbuf + c->stop, ANSI_PREFIX, strlen(ANSI_PREFIX));
468            c->stop += strlen(ANSI_PREFIX);
469            memcpy(c->outbuf + c->stop, server->buffer, server->buflen);
470            c->stop += server->buflen;
471
472            return 0;
473        }
474    }
475
476    /* We no longer have backlog, send our new data */
477
478    /* Send ANSI prefix */
479    ret = nonblock_write(c->fd, ANSI_PREFIX, strlen(ANSI_PREFIX));
480    if(ret == -1)
481    {
482        if(errno == EAGAIN)
483            ret = 0;
484        else
485        {
486            fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno));
487            return -1;
488        }
489    }
490
491    if(ret < (ssize_t)strlen(ANSI_PREFIX))
492    {
493        if(strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER)
494        {
495            /* Overflow! Empty buffer and start again */
496            memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
497            c->start = 0;
498            c->stop = strlen(ANSI_RESET);
499            return 0;
500        }
501
502        memcpy(c->outbuf, ANSI_PREFIX, strlen(ANSI_PREFIX) - ret);
503        c->stop = strlen(ANSI_PREFIX) - ret;
504        memcpy(c->outbuf + c->stop, server->buffer, server->buflen);
505        c->stop += server->buflen;
506
507        return 0;
508    }
509
510    /* Send actual data */
511    ret = nonblock_write(c->fd, server->buffer, server->buflen);
512    if(ret == -1)
513    {
514        if(errno == EAGAIN)
515            ret = 0;
516        else
517        {
518            fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno));
519            return -1;
520        }
521    }
522
523    if(ret < (int)server->buflen)
524    {
525        if(server->buflen > OUTBUFFER)
526        {
527            /* Overflow! Empty buffer and start again */
528            memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
529            c->start = 0;
530            c->stop = strlen(ANSI_RESET);
531            return 0;
532        }
533
534        memcpy(c->outbuf, server->buffer, server->buflen - ret);
535        c->stop = server->buflen - ret;
536
537        return 0;
538    }
539
540    return 0;
541}
542
543ssize_t nonblock_write(int fd, void *buf, size_t len)
544{
545    size_t total = 0;
546    ssize_t ret;
547
548    while(total < len)
549    {
550        do
551        {
552            ret = write(fd, buf, len);
553        }
554        while(ret < 0 && errno == EINTR);
555
556        if(ret < 0)
557            return ret;
558        else if(ret == 0)
559            return total;
560
561        total += len;
562        buf = (void *)((uintptr_t)buf + len);
563    }
564
565    return total;
566}
567
Note: See TracBrowser for help on using the repository browser.