From b79143f3b894c72d915cd83d083cf72f1d2fa154 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sun, 17 Aug 2003 18:38:22 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r868, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/olafmatt/; revision=869 --- flashserver/flashserver.c | 1011 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1011 insertions(+) create mode 100644 flashserver/flashserver.c (limited to 'flashserver') diff --git a/flashserver/flashserver.c b/flashserver/flashserver.c new file mode 100644 index 0000000..e629b6f --- /dev/null +++ b/flashserver/flashserver.c @@ -0,0 +1,1011 @@ +/* -------------------------- flashserver ----------------------------------- */ +/* */ +/* A server for bidirectional communication between Flash and Pd. */ +/* Use XMLsocket() functions in Macromedia Flash 5.0 to connect to it. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/flash/ */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Inspired by work done by Eric Skogen (http://www.cyburrs.com/eskogen/). */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include "s_stuff.h" /* for socket / networking stuff borought from Pd */ +#include "m_imp.h" /* we need more than just m_pd.h */ + +#include +#include +#include +#include +#include +#include +#include +#include /* needs pthread library on win! */ +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#include +#include +#endif + +#define MAX_CONNECT 256 /* maximum number of connected Flash clients */ +#define INBUFSIZE 4096 /* size of receiving data buffer */ +#define DEFSTRSIZE 256 /* default size for strings */ + +static char *version = "flashserver v0.2h :: bidirectional communication between Flash and Pd\n" + " written by Olaf Matthes "; + +/* ----------------------------- flashserver ------------------------- */ + +static t_class *flashserver_class; +static t_binbuf *b; /* a binbuf used to parse output data */ +static char databuf[INBUFSIZE]; /* the string we received from Flash client */ + +typedef void (*t_flashserver_socketnotifier)(void *x); +typedef void (*t_flashserver_socketreceivefn)(void *x, char *buf); + +typedef struct _flashserver +{ + t_object x_obj; + t_outlet *x_msgout; /* output for the received message */ + t_outlet *x_connectout; /* output for number of connections (0 - MAX_CONNECT) */ + t_outlet *x_clientno; /* output for client number (0 - MAX_CONNECT) */ + t_outlet *x_clientsock; /* output for socket number */ + t_outlet *x_connectionip; /* outlet for the IP we just got data from */ + t_canvas *x_canvas; /* needed to get current directory */ + t_symbol *x_host[MAX_CONNECT]; /* list of all hostnames */ + t_int x_fd[MAX_CONNECT]; /* list of all socket numbers */ + t_int x_client[MAX_CONNECT]; /* the client number assigned to each client */ + t_int x_clientlist[MAX_CONNECT]; /* list of client numbers already in use */ + t_int x_sock_fd; /* the socket number we just got data from */ + t_int x_connectsocket; /* number of socket we are listening on */ + t_int x_nconnections; /* total number of open connections */ + t_int x_maxconnect; /* user setable maximum number of connections */ + t_int x_prepend; /* prepend client number to output data */ + t_int x_verbose; +} t_flashserver; + +typedef struct _flashserver_socketreceiver +{ + char *sr_inbuf; /* data we got from clients, a plain character string */ + int sr_inhead; /* writing position in above receive buffer */ + int sr_intail; /* reading position in above receive buffer */ + void *sr_owner; /* we'll put the correcponding t_flashserver in here */ + t_flashserver_socketnotifier sr_notifier; + t_flashserver_socketreceivefn sr_socketreceivefn; +} t_flashserver_socketreceiver; + + /* some prototypes (as needed) */ +static void flashserver_notify(t_flashserver *x); /* notify us in case a client disconnected */ +static void flashserver_client_remove(t_flashserver *x, int sockfd);/* remove client from list */ +static int flashserver_get_client(t_flashserver *x, int sockno); /* find client number */ +static int flashserver_get_socket(t_flashserver *x, int client); /* find socket */ +static int flashserver_get_entry(t_flashserver *x, int sockno); /* find number of entry */ + +/* ----------- socket utility functions ----------- */ + /* set SO_KEEPALIVE option for socket */ +static int flashserver_socket_set_keepalive(int sockfd) +{ + int keepalive = 1; + return setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(int)); +} + + /* set SO_SNDBUF to 0 -> disable send buffer */ +static int flashserver_socket_disable_buffer(int sockfd) +{ + int zero = 0; /* the buffer size */ + return setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&zero, sizeof(zero)); +} + +/* ----------- socket receiver funtions ----------- */ +static t_flashserver_socketreceiver *flashserver_socketreceiver_new(void *owner, t_flashserver_socketnotifier notifier, + t_flashserver_socketreceivefn socketreceivefn) +{ + t_flashserver_socketreceiver *x = (t_flashserver_socketreceiver *)getbytes(sizeof(*x)); + x->sr_inhead = x->sr_intail = 0; + x->sr_owner = owner; + x->sr_notifier = notifier; + x->sr_socketreceivefn = socketreceivefn; + if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_flashserver_socketreceiver"); + return (x); +} + + /* this is in a separately called subroutine so that the buffer isn't + sitting on the stack while the messages are getting passed. */ +static int flashserver_socketreceiver_doread(t_flashserver_socketreceiver *x) +{ + char messbuf[INBUFSIZE], *bp = messbuf; + int indx; + int inhead = x->sr_inhead; + int intail = x->sr_intail; + char *inbuf = x->sr_inbuf; + if (intail == inhead) return (0); + for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1)) + { + char c = *bp++ = inbuf[indx]; + /* we split the buffer at terminating null character */ + if (((c == '\0' || c == '\n') && (!indx || inbuf[indx-1] != '\\'))) + { + intail = (indx+1)&(INBUFSIZE-1); + /* copy data into buffer we pass on to flashserver_doit */ + strncpy(databuf, messbuf, bp - messbuf); + x->sr_inhead = inhead; + x->sr_intail = intail; + return (1); + } + } + return (0); +} + +static void flashserver_socketreceiver_read(t_flashserver_socketreceiver *x, int fd) +{ + char *semi; + int readto = (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1); + int ret; + + t_flashserver *y = x->sr_owner; + + y->x_sock_fd = fd; + + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->sr_inhead) + { + post("flashserver: dropped message"); + x->sr_inhead = x->sr_intail = 0; + readto = INBUFSIZE; + } + else + { + ret = recv(fd, x->sr_inbuf + x->sr_inhead, + readto - x->sr_inhead, 0); + if (ret < 0) + { + sys_sockerror("recv"); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else if (ret == 0) + { + post("flashserver: connection closed on socket %d", fd); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else + { + x->sr_inhead += ret; + if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0; + while (flashserver_socketreceiver_doread(x)) + { + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, databuf); + // else binbuf_eval(inbinbuf, 0, 0, 0); + } + } + } +} + +static void flashserver_socketreceiver_free(t_flashserver_socketreceiver *x) +{ + free(x->sr_inbuf); + freebytes(x, sizeof(*x)); +} + +/* ------------------ main flashserver send stuff ----------------------- */ + +/* flashserver_send_doit() - send data to client, might return an error in + case client disconnected + LATER put this into a seperate thread because send might block, e.g. + in case a client disconnects during send or crashes... */ + +static int flashserver_send_doit(t_flashserver *x, int sockfd, char *buf, int length, int client) +{ + int sent; + for (sent = 0; sent < length;) + { + static double lastwarntime; + static double pleasewarn; + double timebefore = clock_getlogicaltime(); +#ifndef __linux__ + int res = send(sockfd, buf+sent, length-sent, 0); +#else + int res = send(sockfd, buf+sent, length-sent, MSG_DONTWAIT|MSG_NOSIGNAL); +#endif + double timeafter = clock_getlogicaltime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) + { + if (timeafter > lastwarntime + 2) + { + post("flashserver blocked %d msec", + (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } + else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) + { + sys_sockerror("flashserver"); + post("flashserver: could not send data to cient #%d, disconnecting", client+1); + flashserver_client_remove(x, sockfd); + sys_rmpollfn(sockfd); + sys_closesocket(sockfd); + return -1; + } + else + { + sent += res; + } + } + return sent; +} + + /* send XML message consisting of a symbol and a float to client using socket + number, messages are terminated with a null character */ +static void flashserver_sendXML(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sockfd, client = -1, i; + char XMLmsg[DEFSTRSIZE]; + if(x->x_nconnections < 0) + { + post("flashserver: no Flash clients connected"); + return; + } + if(argc < 2) + { + post("flashserver: nothing to send"); + return; + } + /* get socket number of connection (first element in list) */ + if(argv[0].a_type == A_FLOAT) + { + sockfd = atom_getfloatarg(0, argc, argv); + client = flashserver_get_client(x, sockfd); + if(client == -1) /* no Flash client found ? */ + { + post("flashserver: no Flash client on socket %d", sockfd); + return; + } + } + else + { + post("flashserver: no socket specified"); + return; + } + /* process & send data */ + if(sockfd > 0) + { + t_symbol *XMLsym; + t_float XMLvalue; + int length; + /* get symbol and value from input */ + if(argv[1].a_type == A_SYMBOL) + { + XMLsym = atom_getsymbolarg(1, argc, argv); + if(argv[2].a_type == A_FLOAT) + { + XMLvalue = atom_getfloatarg(2, argc, argv); + } + else + { + post("flashserver: no value specified"); + return; + } + } + else + { + post("flashserver: no symbol specified"); + return; + } + /* build up XML message */ + sprintf(XMLmsg, "", XMLsym->s_name, XMLvalue); + + if(x->x_verbose)post("flashserver: sending data to client #%d on socket %d", client + 1, sockfd); + + length = strlen(XMLmsg) + 1; /* lenght of string including terminating null character ! */ + + if(x->x_verbose)post("flashserver: sending XML: \n%s", XMLmsg); + flashserver_send_doit(x, sockfd, XMLmsg, length, client); + + } + else post("flashserver: not a valid socket number (%d)", sockfd); +} + + /* send message to client using socket number, messages are terminated + with a null character, not with A_SEMMI as it would be needed for Pd ! */ +static void flashserver_send(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sockfd, client = -1, i; + if(x->x_nconnections < 0) + { + post("flashserver: no Flash clients connected"); + return; + } + if(argc < 2) + { + post("flashserver: nothing to send"); + return; + } + /* get socket number of connection (first element in list) */ + if(argv[0].a_type == A_FLOAT) + { + sockfd = atom_getfloatarg(0, argc, argv); + for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */ + { + if(x->x_fd[i] == sockfd) + { + client = x->x_client[i]; /* the client we're sending to */ + break; + } + } + if(client == -1) /* still no Flash client found ? */ + { + post("flashserver: no client on socket %d", sockfd); + return; + } + } + else + { + post("flashserver: no socket specified"); + return; + } + /* process & send data */ + if(sockfd > 0) + { + t_binbuf *bb = binbuf_new(); + char *buf; + int length, sent; + + if(x->x_verbose)post("flashserver: sending data to client #%d on socket %d", client+1, sockfd); + + binbuf_add(bb, argc - 1, argv + 1); /* skip first element which is the socket no. */ + binbuf_gettext(bb, &buf, &length); /* convert binbuf into string */ + buf[length] = '\0'; /* append null termination '\0' */ + strcat(buf, ";"); /* add A_SEMI at end of binbuf */ + length = strlen(buf) + 1; /* we send one character more because we must + include the '\0' for Flash ! */ + + if(x->x_verbose)post("flashserver: sending \"%s\"", buf); + // flashserver_check_socket(x, sockfd); + flashserver_send_doit(x, sockfd, buf, length, client); + t_freebytes(buf, length); + binbuf_free(bb); + } + else post("flashserver: not a valid socket number (%d)", sockfd); +} + + /* send XML message to client using client number */ +static void flashserver_client_sendXML(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sock = -1, client, i; + if(x->x_nconnections < 0) + { + post("flashserver: no clients connected"); + return; + } + if(argc < 2) + { + post("flashserver: nothing to send"); + return; + } + /* get number of client (first element in list) */ + if(argv[0].a_type == A_FLOAT) + client = atom_getfloatarg(0, argc, argv); + else + { + post("flashserver: no client specified"); + return; + } + if((sock = flashserver_get_socket(x, client-1)) < 0) + { + post("flashserver: client #%d does not exist", client); + return; + } + argv[0].a_w.w_float = sock; /* replace client # with socket number */ + flashserver_sendXML(x, NULL, argc, argv); /* call send routine with socket number */ +} + + /* send message to client using client number */ +static void flashserver_client_send(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sock = -1, client, i; + if(x->x_nconnections < 0) + { + post("flashserver: no clients connected"); + return; + } + if(argc < 2) + { + post("flashserver: nothing to send"); + return; + } + /* get number of client (first element in list) */ + if(argv[0].a_type == A_FLOAT) + client = atom_getfloatarg(0, argc, argv); + else + { + post("flashserver: no client specified"); + return; + } + if((sock = flashserver_get_socket(x, client-1)) < 0) + { + post("flashserver: client #%d does not exist", client); + return; + } + argv[0].a_w.w_float = sock; /* replace client # with socket number */ + flashserver_send(x, NULL, argc, argv); /* call send routine with socket number */ +} + + /* broadcasts a XML message to all connected Flash clients */ +static void flashserver_broadcastXML(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int i, c = x->x_nconnections; + t_atom at[MAX_CONNECT]; + + if(x->x_nconnections == 0) return; /* check if there are any clients connected */ + + for(i = 0; i < argc; i++) + { + at[i + 1] = argv[i]; + } + argc++; + /* enumerate through the clients and send each socket the message */ + while(c--) + { + SETFLOAT(at, x->x_fd[c]); /* prepend number of client */ + flashserver_sendXML(x, s, argc, at); + } +} + + /* broadcasts a message to all connected Flash clients */ +static void flashserver_broadcast(t_flashserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int i, c = x->x_nconnections; + t_atom at[MAX_CONNECT]; + + if(x->x_nconnections == 0) return; /* check if there are any clients connected */ + + for(i = 0; i < argc; i++) + { + at[i + 1] = argv[i]; + } + argc++; + /* enumerate through the clients and send each socket the message */ + while(c--) + { + SETFLOAT(at, x->x_fd[c]); /* prepend number of client */ + flashserver_send(x, s, argc, at); + } +} + +/* ---------------- main flashserver receive stuff --------------------- */ +static void flashserver_client_remove(t_flashserver *x, int sockfd) +{ + int i, k; + /* remove connection from list */ + for(i = 0; i < x->x_nconnections; i++) + { + if(x->x_fd[i] == sockfd) + { + x->x_nconnections--; + post("flashserver: client #%d (\"%s\") removed from list of clients", x->x_client[i] + 1, x->x_host[i]->s_name); + x->x_host[i] = NULL; /* delete entry */ + x->x_fd[i] = -1; + x->x_clientlist[x->x_client[i]] = -1; /* free client number in list */ + /* rearrange list now: move entries to close the gap */ + for(k = i; k < x->x_nconnections; k++) + { + x->x_host[k] = x->x_host[k + 1]; + x->x_fd[k] = x->x_fd[k + 1]; + x->x_client[k] = x->x_client[k + 1]; + } + } + } + outlet_float(x->x_connectout, x->x_nconnections); +} + /* gets called whenever a client disconnects and removes this + client from the list of clients and reports new number of clients */ +static void flashserver_notify(t_flashserver *x) +{ + flashserver_client_remove(x, x->x_sock_fd); +} + + /* find the entry number for a given socket */ +static int flashserver_get_entry(t_flashserver *x, int sockno) +{ + int i; + /* look at all clients... */ + for(i = 0; i < x->x_nconnections; i++) + { + if(x->x_fd[i] == sockno) + { + return i; + } + } + return -1; /* return an error in case there is no match */ +} + + /* find the client for a given socket number */ +static int flashserver_get_client(t_flashserver *x, int sockno) +{ + int i; + /* look at all clients... */ + for(i = 0; i < x->x_nconnections; i++) /* search for specified socket number */ + { + if(x->x_fd[i] == sockno) + { + return i; /* return the result */ + } + } + return -1; /* return an error in case there is no match */ +} + + /* find the socket for a given client */ +static int flashserver_get_socket(t_flashserver *x, int client) +{ + int i; + /* look at all entries... */ + for(i = 0; i < x->x_nconnections; i++) /* search for specified client number */ + { + if(x->x_client[i] == client) + { + return x->x_fd[i]; /* return the socket */ + } + } + return -1; /* return an error in case there is no match */ +} + + /* kick client number f */ +static void flashserver_client_kick(t_flashserver *x, t_floatarg f) +{ + int client = (int)f; + int sockfd = flashserver_get_socket(x, client-1); /* find socket */ + int i = flashserver_get_entry(x, sockfd); /* find the entry */ + if(sockfd == -1) + { + post("flashserver: client #%d does not exist", client); + return; + } + post("flashserver: kicking client #%d (\"%s\") on socket %d", client, x->x_host[i]->s_name, sockfd); + flashserver_client_remove(x, sockfd); + sys_rmpollfn(sockfd); + sys_closesocket(sockfd); +} + + /* send out socket number and client allocation number */ +static void flashserver_identify(t_flashserver *x) +{ + int i = flashserver_get_client(x, x->x_sock_fd); + if(i == -1) + { + post("flashserver: internal error"); + return; + } + outlet_symbol(x->x_connectionip, x->x_host[i]); /* client's IP address */ + outlet_float(x->x_clientsock, x->x_sock_fd); /* the socket number */ + outlet_float(x->x_clientno, x->x_client[i] + 1); /* the client's allocation number */ +} + +static void flashserver_doit(void *z, char *buf) +{ + t_atom messbuf[1024]; + t_flashserver *x = (t_flashserver *)z; + int msg, natom; + t_atom *at; + int i, length; + + /* check for valid data in this string */ + if(strstr(buf, "GET / HTTP/1.0")) /* oops, got a HTTP request */ + { + post("flashserver: HTTP requests not supported"); + return; + } + else if(strstr(buf, " */ + if(t = strstr(buf, "symbol=\"")) + { + stringlength = strcspn(t+8, "\""); + s = getbytes(stringlength + 1); + strncpy(s, t+8, stringlength); /* copy up to next 'inverted commas' */ + XMLsym = gensym(s); + if(t = strstr(t+8, "value=\"")) + { + XMLvalue = atof(t+7); + /* prepare list for output */ + if(x->x_prepend)SETFLOAT(list, x->x_client[flashserver_get_client(x, x->x_sock_fd)] + 1); + SETSYMBOL(list+x->x_prepend, XMLsym); + SETFLOAT(list+x->x_prepend+1, XMLvalue); + outlet_list(x->x_msgout, 0, 2+x->x_prepend, list); + } + else post("flashserver: got corrupted XML data"); + freebytes(s, stringlength + 1); + } + else post("flashserver: got corrupted XML data"); + } + else if(strstr(buf, ";\0")) /* (probably) got Pd conform data */ + { /* with semi and null termination at the end */ + /* prepend client number if user chose to do so */ + if(x->x_prepend) + { + char buf2[DEFSTRSIZE]; + strcpy(buf2, buf); + sprintf(buf, "%d ", x->x_client[flashserver_get_client(x, x->x_sock_fd)] + 1); + strcat(buf, buf2); + } + /* convert the string buffer we received into a binbuf */ + binbuf_text(b, buf, strlen(buf)); + /* get content out of binbuf */ + natom = binbuf_getnatom(b); + at = binbuf_getvec(b); + /* check again for valid data */ + if(natom < 2) /* need at least 2 atoms: something (float, symbol...) and the semi */ + return; + + flashserver_identify(x); /* output client info */ + + /* process data */ + for (msg = 0; msg < natom;) + { + int emsg; + for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA + && at[emsg].a_type != A_SEMI; emsg++); + + if (emsg > msg) + { + int ii; + for (ii = msg; ii < emsg; ii++) + if (at[ii].a_type == A_DOLLAR || at[ii].a_type == A_DOLLSYM) + { + pd_error(x, "flashserver: got dollar sign in message"); + goto nodice; + } + if (at[msg].a_type == A_FLOAT) + { + if (emsg > msg + 1) + outlet_list(x->x_msgout, 0, emsg-msg, at + msg); + else outlet_float(x->x_msgout, at[msg].a_w.w_float); + } + else if (at[msg].a_type == A_SYMBOL) + outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, + emsg-msg-1, at + msg + 1); + } + nodice: + msg = emsg + 1; + } + } /* endif Pd conform data */ +} + +static void flashserver_connectpoll(t_flashserver *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl); + int i; + if(fd < 0) /* f...ed up socket number */ + { + post("flashserver: accept failed"); + return; + } + else if(x->x_nconnections == x->x_maxconnect) /* too many clients connected */ + { + post("flashserver: refused connection from %s on socket %d, too many clients", + inet_ntoa(incomer_address.sin_addr), fd); + sys_closesocket(fd); + return; + } + else /* everything looks fine, go on... */ + { + t_flashserver_socketreceiver *y = flashserver_socketreceiver_new((void *)x, + (t_flashserver_socketnotifier)flashserver_notify, + (x->x_msgout ? flashserver_doit : 0)); + if(flashserver_socket_set_keepalive(fd)) /* set socket to SO_KEEPALIVE */ + error("flashserver: flashserver_socket_set_keepalive() failed"); + // if(flashserver_socket_disable_buffer(fd)) /* disable send buffer */ + // error("flashserver: flashserver_socket_disable_buffer() failed"); + sys_addpollfn(fd, (t_fdpollfn)flashserver_socketreceiver_read, y); /* add poll-function */ + x->x_nconnections++; + x->x_host[x->x_nconnections - 1] = gensym(inet_ntoa(incomer_address.sin_addr)); + x->x_fd[x->x_nconnections - 1] = fd; + /* search for free client allocation number */ + for(i = 0; i < x->x_maxconnect; i++) + { + if(x->x_clientlist[i] == -1) /* this is the first unused number ! */ + { + x->x_client[x->x_nconnections - 1] = i; + x->x_clientlist[i] = 1; /* mark number as 'used' */ + break; + } + } + + post("flashserver: accepted connection from %s on socket %d", + x->x_host[x->x_nconnections - 1]->s_name, x->x_fd[x->x_nconnections - 1]); + outlet_float(x->x_connectout, x->x_nconnections); + } +} + + /* open the Flash projektor file, code in parts stolen from Joseph A. Sarlo's GriPD */ +static void flashserver_open(t_flashserver *x, t_symbol *sym, int argc, t_atom *argv) +{ + char flashExec[DEFSTRSIZE], filename[DEFSTRSIZE], dirbuf[DEFSTRSIZE], *nameptr; +#ifdef NT + STARTUPINFO si; + PROCESS_INFORMATION pi; + int i; +#else /* not NT */ + int i, pid; +#endif + /* get the filename from message box */ + if (argc > 0) + { + strcpy(filename, argv[0].a_w.w_symbol->s_name); + } + else + { + post("flashserver: no filename specified"); + return; + } + /* now search for the correct path */ + i = open_via_path(canvas_getdir(x->x_canvas)->s_name, filename, "", dirbuf, &nameptr, DEFSTRSIZE, 0); + if(i != -1) + { + close(i); /* open_via_path() has opened the file, we don't want that, so we close it */ + strcpy(flashExec, filename); + if(!strstr(filename, "/") && !strstr(filename, "\\")) /* refabricate the pathname if needed */ + sprintf(filename, "%s/%s", dirbuf, flashExec); + sys_bashfilename(filename, filename); /* convert filename into system conform spelling */ + post("flashserver: going to open \"%s\"", filename); + } + else + { + post("flashserver: \"%s\" not found", filename); + return; + } + +#ifdef NT + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if(strstr(filename, ".swf")) /* is it a .swf file ? */ + sprintf(flashExec, "\"FlashPla.exe %s\"", filename); /* use FlashPlayer -> does not work */ + else + sprintf(flashExec, "\"%s\"", filename); /* add quotation marks */ + if(!CreateProcess(NULL, flashExec, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) != 0) + { + post("flashserver: failed to execute %s", flashExec); + } +#else + pid = fork(); + if (pid == 0) + { + struct sched_param par; + int p1; + + /* Lose setuid priveliges */ + seteuid(getuid()); + /* set lowest priority, SCHED_OTHER policy, unlock mem*/ +#ifdef _POSIX_PRIORITY_SCHEDULING + p1 = sched_get_priority_min(SCHED_OTHER); + par.sched_priority = p1; + if (sched_setscheduler(0,SCHED_OTHER, &par) == -1) + post("flashserver: unable to set priority %d scheduling.", p1); +#endif +#ifdef _POSIX_MEMLOCK + if((munlockall() == -1) && (!getuid())) + post("flashserver: unable to unlock memory."); +#endif + if((execlp(flashExec, flashExec, filename, 0, 0, (char *)0)) == -1) + { + post("flashserver: error launching \"%s\"", flashExec); + exit(1); + } + } +#endif +} + + /* set prepend mode (prepend client allocation number to data) */ +static void flashserver_prepend(t_flashserver *x, t_floatarg f) +{ + if(f) + { + if(!x->x_prepend)post("flashserver~: switching to prepend mode"); + x->x_prepend = 1; + } + else + { + if(x->x_prepend)post("flashserver~: turning off prepend mode"); + x->x_prepend = 0; + } +} + +static void flashserver_print(t_flashserver *x) +{ + int i; + if(x->x_nconnections > 0) + { + post("flashserver: %d open connections:", x->x_nconnections); + + for(i = 0; i < x->x_nconnections; i++) + { + post(" client #%d: \"%s\" on socket %d", + x->x_client[i] + 1, x->x_host[i]->s_name, x->x_fd[i]); + } + } else post("flashserver: no open connections"); +} + +static void *flashserver_new(t_floatarg fportno, t_floatarg fclients, t_floatarg fprep, t_floatarg fverbose) +{ + t_flashserver *x; + int i, keepalive = 1; + struct sockaddr_in server; + int sockfd, portno = (int)fportno; +#ifdef UNIX_SIGPIPE + struct sigaction sa; + /* add our own sigpipe handler to avoid calling Pd's handler */ + sa.sa_handler = SIG_IGN; + /* sa.sa_mask = 0; + sa.sa_flags = 0; + */ + sigaction(SIGPIPE, &sa, (struct sigaction *)NULL); +#endif + /* create a socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); +#if 0 + post("flashserver: receive socket %d", sockfd); +#endif + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + +#ifdef IRIX + /* this seems to work only in IRIX but is unnecessary in + Linux. Not sure what NT needs in place of this. */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) + post("setsockopt failed\n"); +#endif + /* assign server port number */ + server.sin_port = htons((u_short)portno); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) + { + sys_sockerror("bind"); + sys_closesocket(sockfd); + return (0); + } + x = (t_flashserver *)pd_new(flashserver_class); + x->x_msgout = outlet_new(&x->x_obj, &s_anything); + + /* streaming protocol */ + if (listen(sockfd, 5) < 0) + { + sys_sockerror("listen"); + sys_closesocket(sockfd); + sockfd = -1; + } + else + { + sys_addpollfn(sockfd, (t_fdpollfn)flashserver_connectpoll, x); + x->x_connectout = outlet_new(&x->x_obj, &s_float); + x->x_clientno = outlet_new(&x->x_obj, &s_float); + x->x_clientsock = outlet_new(&x->x_obj, &s_float); + x->x_connectionip = outlet_new(&x->x_obj, &s_symbol); + b = binbuf_new(); + } + x->x_connectsocket = sockfd; + x->x_nconnections = 0; + x->x_verbose = 0; + x->x_canvas = canvas_getcurrent(); + + if(fclients) + { + x->x_maxconnect = (t_int)fclients; + if(x->x_maxconnect > MAX_CONNECT) + x->x_maxconnect = MAX_CONNECT; + post("flashserver: set maximum number of clients to %d", x->x_maxconnect); + } + else x->x_maxconnect = MAX_CONNECT; + for(i = 0; i < x->x_maxconnect; i++) + { + x->x_fd[i] = -1; + x->x_client[i] = -1; + x->x_clientlist[i] = -1; + } + + if(fprep) + { + x->x_prepend = 1; + post("flashserver: setting \"prepend mode\""); + } + else x->x_prepend = 0; + + if(fverbose) + { + x->x_verbose = 1; + post("flashserver: debugging mode"); + } + + return (x); +} + +static void flashserver_free(t_flashserver *x) +{ + int i; + post("flashserver: closing open connections...."); + for(i = 0; i < x->x_nconnections; i++) + { + sys_rmpollfn(x->x_fd[i]); + sys_closesocket(x->x_fd[i]); + } + if (x->x_connectsocket >= 0) + { + sys_rmpollfn(x->x_connectsocket); + sys_closesocket(x->x_connectsocket); + } + post("flashserver: ...done"); + binbuf_free(b); +} + +void flashserver_setup(void) +{ + flashserver_class = class_new(gensym("flashserver"),(t_newmethod)flashserver_new, (t_method)flashserver_free, + sizeof(t_flashserver), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(flashserver_class, (t_method)flashserver_print, gensym("print"), 0); + class_addmethod(flashserver_class, (t_method)flashserver_send, gensym("send"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_sendXML, gensym("sendXML"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_client_send, gensym("client"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_client_sendXML, gensym("clientXML"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_broadcast, gensym("broadcast"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_broadcastXML, gensym("broadcastXML"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_open, gensym("open"), A_GIMME, 0); + class_addmethod(flashserver_class, (t_method)flashserver_prepend, gensym("prepend"), A_FLOAT, 0); + class_addmethod(flashserver_class, (t_method)flashserver_client_kick, gensym("kick"), A_FLOAT, 0); + class_sethelpsymbol(flashserver_class, gensym("help-flashserver.pd")); + post(version); +} -- cgit v1.2.1