aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Christoph Steiner <eighthave@users.sourceforge.net>2003-08-17 18:38:22 +0000
committerHans-Christoph Steiner <eighthave@users.sourceforge.net>2003-08-17 18:38:22 +0000
commitb79143f3b894c72d915cd83d083cf72f1d2fa154 (patch)
treee9bb8712ce1039363b0fc9029e79435bb8dc6d5c
parentff6a28aa57fc980c333d45a137f5926db436723b (diff)
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
-rw-r--r--flashserver/flashserver.c1011
1 files changed, 1011 insertions, 0 deletions
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 <olaf.matthes@gmx.de> */
+/* 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 <sys/types.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <pthread.h> /* needs pthread library on win! */
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sched.h>
+#include <sys/mman.h>
+#define SOCKET_ERROR -1
+#else
+#include <io.h>
+#include <fcntl.h>
+#include <winsock.h>
+#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 <olaf.matthes@gmx.de>";
+
+/* ----------------------------- 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, "<data symbol=\"%s\" value=\"%f\" />", 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, "<data")) /* got XML style message */
+ {
+ t_symbol *XMLsym;
+ t_float XMLvalue;
+ char *s;
+ char *t;
+ t_atom list[3]; /* a list containing the symbol and value we got */
+ t_int stringlength;
+
+ flashserver_identify(x); /* output client info */
+ /* parse the XML string:
+ this is a very simple and silly XML parser for XML messages of
+ the following style: <data symbol="<symbol>" value="<float>" /> */
+ 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);
+}