Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / Examples / server.c
dissimilarity index 94%
index d0d0e45..e747a1f 100644 (file)
-/*
- *  WINGs server.c: example how to create a network server using WMConnection
- *
- *  Copyright (c) 2001-2003 Dan Pascu
- *
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <WINGs/WINGs.h>
-
-
-#define _(P)         P
-#define MAXCMD_SIZE  512
-
-
-char *SEConnectionShouldBeRemovedNotification = "SEConnectionShouldBeRemovedNotification";
-
-
-
-
-static void didReceiveInput(ConnectionDelegate *self, WMConnection *cPtr);
-
-static void connectionDidDie(ConnectionDelegate *self, WMConnection *cPtr);
-
-static void connectionDidTimeout(ConnectionDelegate *self, WMConnection *cPtr);
-
-
-extern char *SEConnectionShouldBeRemovedNotification;
-
-static WMUserDefaults *timeDB = NULL;
-static char *ServerAddress = NULL;
-static char *ServerPort = NULL;
-static WMArray *allowedHostList = NULL;
-static WMArray *clientConnections = NULL;
-static WMConnection *serverPtr = NULL;
-
-
-
-static ConnectionDelegate socketDelegate = {
-    NULL,                 /* client data */
-    NULL,                 /* canResumeSending */
-    NULL,                 /* didCatchException */
-    connectionDidDie,     /* didDie */
-    NULL,                 /* didInitialize */
-    didReceiveInput,      /* didReceiveInput */
-    connectionDidTimeout  /* didTimeout */
-};
-
-
-
-void
-wAbort(Bool foo)
-{
-    exit(1);
-}
-
-
-static void
-printHelp(char *progname)
-{
-    printf(_("usage: %s [options]\n\n"), progname);
-    puts(_(" --help                            print this message"));
-    puts(_(" --listen [address:]port   only listen on the specified address/port"));
-    puts(_(" --allow host1[,host2...]  only allow connections from listed hosts\n"));
-    puts(_(" By default server listens on all interfaces and port 34567, unless"
-           " something\nelse is specified with the --listen option. If address is"
-           " omitted or the keyword\n'Any' is used, it will listen on all interfaces else"
-           " only on the specified one.\n\nFor example --listen localhost: will"
-           " listen on the default port 34567, but only\non connections comming"
-           " in through the loopback interface.\n\n Also by default the server"
-           " listens to incoming connections from any host,\nunless a list of"
-           " hosts is given with the --allow option, in which case it will\nreject"
-           " connections not comming from those hosts.\nThe list of hosts is comma"
-           " separated and should NOT contain ANY spaces."));
-}
-
-
-static void
-enqueueConnectionForRemoval(WMConnection *cPtr)
-{
-    WMNotification *notif;
-
-    /*don't release notif here. it will be released by queue after processing */
-    notif = WMCreateNotification(SEConnectionShouldBeRemovedNotification,
-                                 cPtr, NULL);
-    WMEnqueueNotification(WMGetDefaultNotificationQueue(), notif, WMPostASAP);
-}
-
-
-static int
-sendMessage(WMConnection *cPtr, char *message)
-{
-    WMData *aData;
-    int res;
-
-    if (WMGetConnectionState(cPtr)!=WCConnected)
-        return -1;
-
-    aData = WMCreateDataWithBytes(message, strlen(message));
-    res = WMSendConnectionData(cPtr, aData);
-    WMReleaseData(aData);
-
-    return res;
-}
-
-
-static Bool
-enqueueMessage(WMConnection *cPtr, char *message)
-{
-    WMData *aData;
-    Bool res;
-
-    if (WMGetConnectionState(cPtr)!=WCConnected)
-        return False;
-
-    aData = WMCreateDataWithBytes(message, strlen(message));
-    res = WMEnqueueConnectionData(cPtr, aData);
-    WMReleaseData(aData);
-
-    return res;
-}
-
-
-static char*
-findDelimiter(char *data, const char *endPtr)
-{
-    wassertrv(data < endPtr, NULL);
-
-    while (data<endPtr && *data!='\n' && *data!='\r' && *data!=';' && *data!='\0')
-        data++;
-
-    if (data < endPtr)
-        return data;
-
-    return NULL;
-}
-
-
-static WMArray*
-getAvailableMessages(WMConnection *cPtr)
-{
-    char *ptr, *crtPos, *buffer;
-    const char *bytes, *endPtr;
-    WMData *aData, *receivedData, *holdData;
-    WMRange range;
-    WMArray *messages;
-    int length;
-
-    receivedData = WMGetConnectionAvailableData(cPtr);
-    if (!receivedData)
-        return NULL;
-    if ((length=WMGetDataLength(receivedData))==0) {
-        WMReleaseData(receivedData);
-        return NULL;
-    }
-
-    holdData = (WMData*)WMGetConnectionClientData(cPtr);
-    if (holdData) {
-        WMAppendData(holdData, receivedData);
-        WMReleaseData(receivedData);
-        WMSetConnectionClientData(cPtr, NULL);
-        aData = holdData;
-    } else {
-        aData = receivedData;
-    }
-
-    length = WMGetDataLength(aData);
-    bytes = (char*)WMDataBytes(aData);
-    endPtr = bytes + length;
-
-    messages = WMCreateArrayWithDestructor(1, wfree);
-    crtPos = (char*)bytes;
-    while (crtPos<endPtr && (ptr = findDelimiter(crtPos, endPtr))!=NULL) {
-        range.position = (crtPos - bytes);
-        range.count = (ptr - crtPos);
-        if (range.count > MAXCMD_SIZE) {
-            /* Hmmm... The message is too long. Possibly that someone is
-             * flooding us, or there is a dumb client which do not know
-             * who is talking to. */
-            sendMessage(cPtr, "Command too long\n\r");
-            WMFreeArray(messages);
-            WMReleaseData(aData);
-            WMCloseConnection(cPtr);
-            enqueueConnectionForRemoval(cPtr);
-            return NULL;
-        }
-        buffer = wmalloc(range.count+1);
-        WMGetDataBytesWithRange(aData, buffer, range);
-        buffer[range.count] = '\0';
-        WMAddToArray(messages, buffer);
-        crtPos = ptr;
-        while (crtPos<endPtr && (*crtPos=='\n' || *crtPos=='\r' ||
-                                 *crtPos=='\t' || *crtPos=='\0' ||
-                                 *crtPos==';' || *crtPos==' ')) {
-            crtPos++;
-        }
-    }
-
-    if (crtPos<endPtr) {
-        range.position = (crtPos - bytes);
-        range.count = (endPtr - crtPos);
-        if (range.count > MAXCMD_SIZE) {
-            /* Flooooooding!!!! */
-            sendMessage(cPtr, "Message too long\n\r");
-            WMFreeArray(messages);
-            WMReleaseData(aData);
-            WMCloseConnection(cPtr);
-            enqueueConnectionForRemoval(cPtr);
-            return NULL;
-        }
-        holdData = WMGetSubdataWithRange(aData, range);
-        WMSetConnectionClientData(cPtr, holdData);
-    }
-    WMReleaseData(aData);
-
-    if (WMGetArrayItemCount(messages)==0) {
-        WMFreeArray(messages);
-        messages = NULL;
-    }
-    return messages;
-}
-
-
-
-static void
-complainAboutBadArgs(WMConnection *cPtr, char *cmdName, char *badArgs)
-{
-    char *buf = wmalloc(strlen(cmdName) + strlen(badArgs) + 100);
-
-    sprintf(buf, _("Invalid parameters '%s' for command %s. Use HELP for"
-                   " a list of commands.\n"), badArgs, cmdName);
-    sendMessage(cPtr, buf);
-    wfree(buf);
-}
-
-
-static void
-sendUpdateMessage(WMConnection *cPtr, char *id, int time)
-{
-    char *buf = wmalloc(strlen(id) + 100);
-
-    sprintf(buf, "%s has %i minutes left\n", id, time);
-    sendMessage(cPtr, buf);
-    wfree(buf);
-}
-
-
-static void
-showId(WMConnection *cPtr)
-{
-    sendMessage(cPtr, "Server example based on WMConnection\n");
-}
-
-
-static void
-showHelp(WMConnection *cPtr)
-{
-    char *buf = wmalloc(strlen(WMGetApplicationName()) + 16);
-
-    sprintf(buf, _("%s commands:\n\n"), WMGetApplicationName());
-
-    enqueueMessage(cPtr, _("\n"));
-    enqueueMessage(cPtr, buf);
-    enqueueMessage(cPtr, _("GET <id>\t- return time left (in minutes) "
-                           "for user with id <id>\n"));
-    enqueueMessage(cPtr, _("SET <id> <time>\t- set time limit to <time> "
-                           "minutes for user with id <id>\n"));
-    enqueueMessage(cPtr, _("ADD <id> <time>\t- add <time> minutes "
-                           "for user with id <id>\n"));
-    enqueueMessage(cPtr, _("SUB <id> <time>\t- subtract <time> minutes "
-                           "for user with id <id>\n"));
-    enqueueMessage(cPtr, _("REMOVE <id>\t- remove time limitations for "
-                           "user with id <id>\n"));
-    enqueueMessage(cPtr, _("LIST\t\t- list all users and their "
-                           "corresponding time limit\n"));
-    enqueueMessage(cPtr, _("ID\t\t- returns the Time Manager "
-                           "identification string\n"));
-    enqueueMessage(cPtr, _("EXIT\t\t- exits session\n"));
-    enqueueMessage(cPtr, _("QUIT\t\t- exits session\n"));
-    enqueueMessage(cPtr, _("HELP\t\t- show this message\n\n"));
-    /* Just flush the queue we made before */
-    WMFlushConnection(cPtr);
-    wfree(buf);
-}
-
-
-static void
-listUsers(WMConnection *cPtr)
-{
-    WMPropList *userList;
-    char *id;
-    int i, time;
-
-    userList = WMGetUDKeys(timeDB);
-
-    for (i=0; i<WMGetPropListItemCount(userList); i++) {
-        id = WMGetFromPLString(WMGetFromPLArray(userList, i));
-        time = WMGetUDIntegerForKey(timeDB, id);
-        sendUpdateMessage(cPtr, id, time);
-    }
-
-    WMReleasePropList(userList);
-}
-
-
-static void
-setTimeForUser(WMConnection *cPtr, char *cmdArgs)
-{
-    char *id;
-    int i, time;
-
-    id = wmalloc(strlen(cmdArgs));
-    if (sscanf(cmdArgs, "%s %d", id, &time)!=2) {
-        complainAboutBadArgs(cPtr, "SET", cmdArgs);
-        wfree(id);
-        return;
-    }
-    if (time<0)
-        time = 0;
-
-    WMSetUDIntegerForKey(timeDB, time, id);
-
-    for (i=0; i<WMGetArrayItemCount(clientConnections); i++) {
-        cPtr = WMGetFromArray(clientConnections, i);
-        sendUpdateMessage(cPtr, id, time);
-    }
-    wfree(id);
-}
-
-
-static void
-addTimeToUser(WMConnection *cPtr, char *cmdArgs)
-{
-    char *id;
-    int i, time, newTime;
-
-    id = wmalloc(strlen(cmdArgs));
-    if (sscanf(cmdArgs, "%s %d", id, &time)!=2) {
-        complainAboutBadArgs(cPtr, "ADD", cmdArgs);
-        wfree(id);
-        return;
-    }
-
-    newTime = WMGetUDIntegerForKey(timeDB, id) + time;
-    if (newTime<0)
-        newTime = 0;
-
-    WMSetUDIntegerForKey(timeDB, newTime, id);
-
-    for (i=0; i<WMGetArrayItemCount(clientConnections); i++) {
-        cPtr = WMGetFromArray(clientConnections, i);
-        sendUpdateMessage(cPtr, id, newTime);
-    }
-    wfree(id);
-}
-
-
-static void
-subTimeFromUser(WMConnection *cPtr, char *cmdArgs)
-{
-    char *id;
-    int i, time, newTime;
-
-    id = wmalloc(strlen(cmdArgs));
-    if (sscanf(cmdArgs, "%s %d", id, &time)!=2) {
-        complainAboutBadArgs(cPtr, "SUB", cmdArgs);
-        wfree(id);
-        return;
-    }
-
-    newTime = WMGetUDIntegerForKey(timeDB, id) - time;
-    if (newTime<0)
-        newTime = 0;
-
-    WMSetUDIntegerForKey(timeDB, newTime, id);
-
-    for (i=0; i<WMGetArrayItemCount(clientConnections); i++) {
-        cPtr = WMGetFromArray(clientConnections, i);
-        sendUpdateMessage(cPtr, id, newTime);
-    }
-    wfree(id);
-}
-
-
-static void
-removeTimeForUser(WMConnection *cPtr, char *cmdArgs)
-{
-    char *ptr;
-    int i;
-
-    if (cmdArgs[0]=='\0') {
-        sendMessage(cPtr, _("Missing parameter for command REMOVE."
-                            " Use HELP for a list of commands.\n"));
-        return;
-    }
-
-    ptr = cmdArgs;
-    while (*ptr && *ptr!=' ' && *ptr!='\t')
-        ptr++;
-    *ptr = '\0';
-
-    WMRemoveUDObjectForKey(timeDB, cmdArgs);
-
-    for (i=0; i<WMGetArrayItemCount(clientConnections); i++) {
-        cPtr = WMGetFromArray(clientConnections, i);
-        sendUpdateMessage(cPtr, cmdArgs, -1);
-    }
-}
-
-
-static void
-getTimeForUser(WMConnection *cPtr, char *cmdArgs)
-{
-    char *ptr;
-    int time;
-
-    if (cmdArgs[0]=='\0') {
-        sendMessage(cPtr, _("Missing parameter for command GET."
-                            " Use HELP for a list of commands.\n"));
-        return;
-    }
-
-    ptr = cmdArgs;
-    while (*ptr && *ptr!=' ' && *ptr!='\t')
-        ptr++;
-    *ptr = '\0';
-
-    if (WMGetUDObjectForKey(timeDB, cmdArgs)!=NULL)
-        time = WMGetUDIntegerForKey(timeDB, cmdArgs);
-    else
-        time = -1;
-
-    sendUpdateMessage(cPtr, cmdArgs, time);
-}
-
-
-static void
-handleConnection(WMConnection *cPtr)
-{
-    char *command, *ptr, *cmdArgs, *buffer;
-    WMArray *commands;
-    int i;
-
-    commands = getAvailableMessages(cPtr);
-    if (!commands)
-        return;
-
-    for (i=0; i<WMGetArrayItemCount(commands); i++) {
-        command = WMGetFromArray(commands, i);
-        while (*command && (*command==' ' || *command=='\t'))
-            command++;
-        ptr = command;
-        while(*ptr && *ptr!=' ' && *ptr!='\t')
-            ptr++;
-        if (*ptr) {
-            *ptr = '\0';
-            ptr++;
-        }
-        while (*ptr && (*ptr==' ' || *ptr=='\t'))
-            ptr++;
-
-        cmdArgs = ptr;
-
-        fprintf(stderr, "Command: '%s', args: '%s'\n", command, cmdArgs);
-
-        if (strcasecmp(command, "quit")==0 || strcasecmp(command, "exit")==0) {
-            sendMessage(cPtr, "Bye\n");
-            WMCloseConnection(cPtr);
-            enqueueConnectionForRemoval(cPtr);
-            WMFreeArray(commands);
-            return;
-        } else if (strcasecmp(command, "id")==0) {
-            showId(cPtr);
-        } else if (strcasecmp(command, "help")==0) {
-            showHelp(cPtr);
-        } else if (strcasecmp(command, "list")==0) {
-            listUsers(cPtr);
-        } else if (strcasecmp(command, "set")==0) {
-            setTimeForUser(cPtr, cmdArgs);
-        } else if (strcasecmp(command, "add")==0) {
-            addTimeToUser(cPtr, cmdArgs);
-        } else if (strcasecmp(command, "sub")==0) {
-            subTimeFromUser(cPtr, cmdArgs);
-        } else if (strcasecmp(command, "remove")==0) {
-            removeTimeForUser(cPtr, cmdArgs);
-        } else if (strcasecmp(command, "get")==0) {
-            getTimeForUser(cPtr, cmdArgs);
-        } else {
-            buffer = wmalloc(strlen(command) + 100);
-            sprintf(buffer, _("Unknown command '%s'. Try HELP for"
-                              " a list of commands.\n"), command);
-            sendMessage(cPtr, buffer);
-            wfree(buffer);
-        }
-    }
-
-    WMFreeArray(commands);
-}
-
-
-static Bool
-isAllowedToConnect(WMConnection *cPtr)
-{
-    WMHost *hPtr;
-    int i;
-
-    if (allowedHostList == NULL)
-        return True; /* No list. Allow all by default */
-
-    hPtr = WMGetHostWithAddress(WMGetConnectionAddress(cPtr));
-    for (i=0; i<WMGetArrayItemCount(allowedHostList); i++) {
-        if (WMIsHostEqualToHost(hPtr, WMGetFromArray(allowedHostList, i))) {
-            WMReleaseHost(hPtr);
-            return True;
-        }
-    }
-
-    WMReleaseHost(hPtr);
-
-    return False;
-}
-
-
-static void
-didReceiveInput(ConnectionDelegate *self, WMConnection *cPtr)
-{
-    if (cPtr == serverPtr) {
-        WMConnection *newPtr = WMAcceptConnection(cPtr);
-
-        if (newPtr) {
-            if (isAllowedToConnect(newPtr)) {
-                WMSetConnectionDelegate(newPtr, &socketDelegate);
-                WMSetConnectionSendTimeout(newPtr, 120);
-                WMAddToArray(clientConnections, newPtr);
-            } else {
-                sendMessage(newPtr, "Sorry, you are not allowed to connect.\n");
-                WMDestroyConnection(newPtr);
-            }
-        }
-    } else {
-        /* Data arriving on an already-connected socket */
-        handleConnection(cPtr);
-    }
-}
-
-
-static void
-connectionDidTimeout(ConnectionDelegate *self, WMConnection *cPtr)
-{
-    WMHost *hPtr;
-
-    if (cPtr == serverPtr) {
-        wfatal(_("The server listening socket did timeout. Exiting."));
-        exit(1);
-    }
-
-    hPtr = WMGetHostWithAddress(WMGetConnectionAddress(cPtr));
-    wwarning(_("Connection with %s did timeout. Closing connection."),
-             WMGetHostName(hPtr));
-    WMReleaseHost(hPtr);
-
-    enqueueConnectionForRemoval(cPtr);
-}
-
-
-static void
-connectionDidDie(ConnectionDelegate *self, WMConnection *cPtr)
-{
-    if (cPtr == serverPtr) {
-        /* trouble. server listening port itself died!!! */
-        wfatal(_("The server listening socket died. Exiting."));
-        exit(1);
-    }
-
-    enqueueConnectionForRemoval(cPtr);
-}
-
-
-static void
-removeConnection(void *observer, WMNotification *notification)
-{
-    WMConnection *cPtr = (WMConnection*)WMGetNotificationObject(notification);
-    WMData *data;
-
-    WMRemoveFromArray(clientConnections, cPtr);
-    if ((data = (WMData*)WMGetConnectionClientData(cPtr))!=NULL)
-        WMReleaseData(data);
-    WMDestroyConnection(cPtr);
-}
-
-static void
-updatedDomain(void *observer, WMNotification *notification)
-{
-    wmessage("defaults domain file changed on disk. synchronizing.");
-}
-
-
-#if 0
-static Bool
-isDifferent(char *str1, char *str2)
-{
-    if ((!str1 && !str2) || (str1 && str2 && strcmp(str1, str2)==0))
-        return False;
-
-    return True;
-}
-#endif
-
-
-int
-main(int argc, char **argv)
-{
-    int i;
-
-    wsetabort(wAbort);
-
-    WMInitializeApplication("server", &argc, argv);
-
-    if (argc>1) {
-        for (i=1; i<argc; i++) {
-            if (strcmp(argv[i], "--help")==0) {
-                printHelp(argv[0]);
-                exit(0);
-            } else if (strcmp(argv[i], "--listen")==0) {
-                char *p;
-
-                if ((p = strchr(argv[++i], ':')) != NULL) {
-                    *p = 0;
-                    ServerAddress = wstrdup(argv[i]);
-                    ServerPort = wstrdup(p+1);
-                    *p = ':';
-                    if (ServerAddress[0] == 0) {
-                        wfree(ServerAddress);
-                        ServerAddress = NULL;
-                    }
-                    if (ServerPort[0] == 0) {
-                        wfree(ServerPort);
-                        ServerPort = "34567";
-                    }
-                } else if (argv[i][0]!=0) {
-                    ServerPort = argv[i];
-                }
-            } else if (strcmp(argv[i], "--allow")==0) {
-                char *p, *ptr;
-                int done;
-                WMHost *hPtr;
-
-                ptr = argv[++i];
-                done = 0;
-                while (!done) {
-                    if ((p = strchr(ptr, ',')) != NULL) {
-                        *p = 0;
-                    }
-                    if (*ptr != 0) {
-                        hPtr = WMGetHostWithName(ptr);
-                        if (hPtr) {
-                            if (!allowedHostList)
-                                allowedHostList = WMCreateArray(4);
-                            WMAddToArray(allowedHostList, hPtr);
-                        } else {
-                            wwarning(_("Unknown host '%s'. Ignored."), ptr);
-                        }
-                    }
-
-                    if (p!=NULL) {
-                        *p = ',';
-                        ptr = p+1;
-                    } else {
-                        done = 1;
-                    }
-                }
-            } else {
-                printf(_("%s: invalid argument '%s'\n"), argv[0], argv[i]);
-                printf(_("Try '%s --help' for more information\n"), argv[0]);
-                exit(1);
-            }
-        }
-    }
-
-    timeDB = WMGetDefaultsFromPath("./UserTime.plist");
-    WMAddNotificationObserver(updatedDomain, NULL,
-                              WMUserDefaultsDidChangeNotification, NULL);
-
-    clientConnections = WMCreateArray(4);
-
-    /* A NULL ServerAddress means to listen on any address the host has.
-     * Else if ServerAddress points to a specific address (like "localhost",
-     * "host.domain.com" or "192.168.1.1"), then it will only listen on that
-     * interface and ignore incoming connections on the others. */
-    if (ServerAddress && strcasecmp(ServerAddress, "Any")==0)
-        ServerAddress = NULL;
-    if (ServerPort==NULL)
-        ServerPort = "34567";
-
-    printf("Server will listen on '%s:%s'\n", ServerAddress?ServerAddress:"Any",
-           ServerPort);
-    printf("This server will allow connections from:");
-    if (allowedHostList) {
-        int i;
-        char *hName;
-
-        for (i=0; i<WMGetArrayItemCount(allowedHostList); i++) {
-            hName = WMGetHostName(WMGetFromArray(allowedHostList, i));
-            printf("%s'%s'", i==0?" ":", ", hName);
-        }
-        printf(".\n");
-    } else {
-        printf(" any host.\n");
-    }
-
-    serverPtr = WMCreateConnectionAsServerAtAddress(ServerAddress, ServerPort,
-                                                    NULL);
-
-    if (!serverPtr) {
-        wfatal("could not create server on `%s:%s`. Exiting.",
-               ServerAddress ? ServerAddress : "localhost", ServerPort);
-        exit(1);
-    }
-
-    WMSetConnectionDelegate(serverPtr, &socketDelegate);
-
-    WMAddNotificationObserver(removeConnection, NULL,
-                              SEConnectionShouldBeRemovedNotification, NULL);
-
-    while (1) {
-        /* The ASAP notification queue is called at the end of WHandleEvents()
-         * There's where died connections we get while running through
-         * WHandleEvents() get removed. */
-        WHandleEvents();
-    }
-
-    return 0;
-}
-
-
+/*
+ *  WINGs server.c: example how to create a network server using WMConnection
+ *
+ *  Copyright (c) 2001-2003 Dan Pascu
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <WINGs/WINGs.h>
+
+#define _(P)         P
+#define MAXCMD_SIZE  512
+
+char *SEConnectionShouldBeRemovedNotification = "SEConnectionShouldBeRemovedNotification";
+
+static void didReceiveInput(ConnectionDelegate * self, WMConnection * cPtr);
+
+static void connectionDidDie(ConnectionDelegate * self, WMConnection * cPtr);
+
+static void connectionDidTimeout(ConnectionDelegate * self, WMConnection * cPtr);
+
+extern char *SEConnectionShouldBeRemovedNotification;
+
+static WMUserDefaults *timeDB = NULL;
+static char *ServerAddress = NULL;
+static char *ServerPort = NULL;
+static WMArray *allowedHostList = NULL;
+static WMArray *clientConnections = NULL;
+static WMConnection *serverPtr = NULL;
+
+static ConnectionDelegate socketDelegate = {
+       NULL,                   /* client data */
+       NULL,                   /* canResumeSending */
+       NULL,                   /* didCatchException */
+       connectionDidDie,       /* didDie */
+       NULL,                   /* didInitialize */
+       didReceiveInput,        /* didReceiveInput */
+       connectionDidTimeout    /* didTimeout */
+};
+
+void wAbort(Bool foo)
+{
+       exit(1);
+}
+
+static void printHelp(char *progname)
+{
+       printf(_("usage: %s [options]\n\n"), progname);
+       puts(_(" --help                         print this message"));
+       puts(_(" --listen [address:]port        only listen on the specified address/port"));
+       puts(_(" --allow host1[,host2...]       only allow connections from listed hosts\n"));
+       puts(_(" By default server listens on all interfaces and port 34567, unless"
+              " something\nelse is specified with the --listen option. If address is"
+              " omitted or the keyword\n'Any' is used, it will listen on all interfaces else"
+              " only on the specified one.\n\nFor example --listen localhost: will"
+              " listen on the default port 34567, but only\non connections comming"
+              " in through the loopback interface.\n\n Also by default the server"
+              " listens to incoming connections from any host,\nunless a list of"
+              " hosts is given with the --allow option, in which case it will\nreject"
+              " connections not comming from those hosts.\nThe list of hosts is comma"
+              " separated and should NOT contain ANY spaces."));
+}
+
+static void enqueueConnectionForRemoval(WMConnection * cPtr)
+{
+       WMNotification *notif;
+
+       /*don't release notif here. it will be released by queue after processing */
+       notif = WMCreateNotification(SEConnectionShouldBeRemovedNotification, cPtr, NULL);
+       WMEnqueueNotification(WMGetDefaultNotificationQueue(), notif, WMPostASAP);
+}
+
+static int sendMessage(WMConnection * cPtr, char *message)
+{
+       WMData *aData;
+       int res;
+
+       if (WMGetConnectionState(cPtr) != WCConnected)
+               return -1;
+
+       aData = WMCreateDataWithBytes(message, strlen(message));
+       res = WMSendConnectionData(cPtr, aData);
+       WMReleaseData(aData);
+
+       return res;
+}
+
+static Bool enqueueMessage(WMConnection * cPtr, char *message)
+{
+       WMData *aData;
+       Bool res;
+
+       if (WMGetConnectionState(cPtr) != WCConnected)
+               return False;
+
+       aData = WMCreateDataWithBytes(message, strlen(message));
+       res = WMEnqueueConnectionData(cPtr, aData);
+       WMReleaseData(aData);
+
+       return res;
+}
+
+static char *findDelimiter(char *data, const char *endPtr)
+{
+       wassertrv(data < endPtr, NULL);
+
+       while (data < endPtr && *data != '\n' && *data != '\r' && *data != ';' && *data != '\0')
+               data++;
+
+       if (data < endPtr)
+               return data;
+
+       return NULL;
+}
+
+static WMArray *getAvailableMessages(WMConnection * cPtr)
+{
+       char *ptr, *crtPos, *buffer;
+       const char *bytes, *endPtr;
+       WMData *aData, *receivedData, *holdData;
+       WMRange range;
+       WMArray *messages;
+       int length;
+
+       receivedData = WMGetConnectionAvailableData(cPtr);
+       if (!receivedData)
+               return NULL;
+       if ((length = WMGetDataLength(receivedData)) == 0) {
+               WMReleaseData(receivedData);
+               return NULL;
+       }
+
+       holdData = (WMData *) WMGetConnectionClientData(cPtr);
+       if (holdData) {
+               WMAppendData(holdData, receivedData);
+               WMReleaseData(receivedData);
+               WMSetConnectionClientData(cPtr, NULL);
+               aData = holdData;
+       } else {
+               aData = receivedData;
+       }
+
+       length = WMGetDataLength(aData);
+       bytes = (char *)WMDataBytes(aData);
+       endPtr = bytes + length;
+
+       messages = WMCreateArrayWithDestructor(1, wfree);
+       crtPos = (char *)bytes;
+       while (crtPos < endPtr && (ptr = findDelimiter(crtPos, endPtr)) != NULL) {
+               range.position = (crtPos - bytes);
+               range.count = (ptr - crtPos);
+               if (range.count > MAXCMD_SIZE) {
+                       /* Hmmm... The message is too long. Possibly that someone is
+                        * flooding us, or there is a dumb client which do not know
+                        * who is talking to. */
+                       sendMessage(cPtr, "Command too long\n\r");
+                       WMFreeArray(messages);
+                       WMReleaseData(aData);
+                       WMCloseConnection(cPtr);
+                       enqueueConnectionForRemoval(cPtr);
+                       return NULL;
+               }
+               buffer = wmalloc(range.count + 1);
+               WMGetDataBytesWithRange(aData, buffer, range);
+               buffer[range.count] = '\0';
+               WMAddToArray(messages, buffer);
+               crtPos = ptr;
+               while (crtPos < endPtr && (*crtPos == '\n' || *crtPos == '\r' ||
+                                          *crtPos == '\t' || *crtPos == '\0' ||
+                                          *crtPos == ';' || *crtPos == ' ')) {
+                       crtPos++;
+               }
+       }
+
+       if (crtPos < endPtr) {
+               range.position = (crtPos - bytes);
+               range.count = (endPtr - crtPos);
+               if (range.count > MAXCMD_SIZE) {
+                       /* Flooooooding!!!! */
+                       sendMessage(cPtr, "Message too long\n\r");
+                       WMFreeArray(messages);
+                       WMReleaseData(aData);
+                       WMCloseConnection(cPtr);
+                       enqueueConnectionForRemoval(cPtr);
+                       return NULL;
+               }
+               holdData = WMGetSubdataWithRange(aData, range);
+               WMSetConnectionClientData(cPtr, holdData);
+       }
+       WMReleaseData(aData);
+
+       if (WMGetArrayItemCount(messages) == 0) {
+               WMFreeArray(messages);
+               messages = NULL;
+       }
+       return messages;
+}
+
+static void complainAboutBadArgs(WMConnection * cPtr, char *cmdName, char *badArgs)
+{
+       char *buf = wmalloc(strlen(cmdName) + strlen(badArgs) + 100);
+
+       sprintf(buf, _("Invalid parameters '%s' for command %s. Use HELP for"
+                      " a list of commands.\n"), badArgs, cmdName);
+       sendMessage(cPtr, buf);
+       wfree(buf);
+}
+
+static void sendUpdateMessage(WMConnection * cPtr, char *id, int time)
+{
+       char *buf = wmalloc(strlen(id) + 100);
+
+       sprintf(buf, "%s has %i minutes left\n", id, time);
+       sendMessage(cPtr, buf);
+       wfree(buf);
+}
+
+static void showId(WMConnection * cPtr)
+{
+       sendMessage(cPtr, "Server example based on WMConnection\n");
+}
+
+static void showHelp(WMConnection * cPtr)
+{
+       char *buf = wmalloc(strlen(WMGetApplicationName()) + 16);
+
+       sprintf(buf, _("%s commands:\n\n"), WMGetApplicationName());
+
+       enqueueMessage(cPtr, _("\n"));
+       enqueueMessage(cPtr, buf);
+       enqueueMessage(cPtr, _("GET <id>\t- return time left (in minutes) " "for user with id <id>\n"));
+       enqueueMessage(cPtr, _("SET <id> <time>\t- set time limit to <time> " "minutes for user with id <id>\n"));
+       enqueueMessage(cPtr, _("ADD <id> <time>\t- add <time> minutes " "for user with id <id>\n"));
+       enqueueMessage(cPtr, _("SUB <id> <time>\t- subtract <time> minutes " "for user with id <id>\n"));
+       enqueueMessage(cPtr, _("REMOVE <id>\t- remove time limitations for " "user with id <id>\n"));
+       enqueueMessage(cPtr, _("LIST\t\t- list all users and their " "corresponding time limit\n"));
+       enqueueMessage(cPtr, _("ID\t\t- returns the Time Manager " "identification string\n"));
+       enqueueMessage(cPtr, _("EXIT\t\t- exits session\n"));
+       enqueueMessage(cPtr, _("QUIT\t\t- exits session\n"));
+       enqueueMessage(cPtr, _("HELP\t\t- show this message\n\n"));
+       /* Just flush the queue we made before */
+       WMFlushConnection(cPtr);
+       wfree(buf);
+}
+
+static void listUsers(WMConnection * cPtr)
+{
+       WMPropList *userList;
+       char *id;
+       int i, time;
+
+       userList = WMGetUDKeys(timeDB);
+
+       for (i = 0; i < WMGetPropListItemCount(userList); i++) {
+               id = WMGetFromPLString(WMGetFromPLArray(userList, i));
+               time = WMGetUDIntegerForKey(timeDB, id);
+               sendUpdateMessage(cPtr, id, time);
+       }
+
+       WMReleasePropList(userList);
+}
+
+static void setTimeForUser(WMConnection * cPtr, char *cmdArgs)
+{
+       char *id;
+       int i, time;
+
+       id = wmalloc(strlen(cmdArgs));
+       if (sscanf(cmdArgs, "%s %d", id, &time) != 2) {
+               complainAboutBadArgs(cPtr, "SET", cmdArgs);
+               wfree(id);
+               return;
+       }
+       if (time < 0)
+               time = 0;
+
+       WMSetUDIntegerForKey(timeDB, time, id);
+
+       for (i = 0; i < WMGetArrayItemCount(clientConnections); i++) {
+               cPtr = WMGetFromArray(clientConnections, i);
+               sendUpdateMessage(cPtr, id, time);
+       }
+       wfree(id);
+}
+
+static void addTimeToUser(WMConnection * cPtr, char *cmdArgs)
+{
+       char *id;
+       int i, time, newTime;
+
+       id = wmalloc(strlen(cmdArgs));
+       if (sscanf(cmdArgs, "%s %d", id, &time) != 2) {
+               complainAboutBadArgs(cPtr, "ADD", cmdArgs);
+               wfree(id);
+               return;
+       }
+
+       newTime = WMGetUDIntegerForKey(timeDB, id) + time;
+       if (newTime < 0)
+               newTime = 0;
+
+       WMSetUDIntegerForKey(timeDB, newTime, id);
+
+       for (i = 0; i < WMGetArrayItemCount(clientConnections); i++) {
+               cPtr = WMGetFromArray(clientConnections, i);
+               sendUpdateMessage(cPtr, id, newTime);
+       }
+       wfree(id);
+}
+
+static void subTimeFromUser(WMConnection * cPtr, char *cmdArgs)
+{
+       char *id;
+       int i, time, newTime;
+
+       id = wmalloc(strlen(cmdArgs));
+       if (sscanf(cmdArgs, "%s %d", id, &time) != 2) {
+               complainAboutBadArgs(cPtr, "SUB", cmdArgs);
+               wfree(id);
+               return;
+       }
+
+       newTime = WMGetUDIntegerForKey(timeDB, id) - time;
+       if (newTime < 0)
+               newTime = 0;
+
+       WMSetUDIntegerForKey(timeDB, newTime, id);
+
+       for (i = 0; i < WMGetArrayItemCount(clientConnections); i++) {
+               cPtr = WMGetFromArray(clientConnections, i);
+               sendUpdateMessage(cPtr, id, newTime);
+       }
+       wfree(id);
+}
+
+static void removeTimeForUser(WMConnection * cPtr, char *cmdArgs)
+{
+       char *ptr;
+       int i;
+
+       if (cmdArgs[0] == '\0') {
+               sendMessage(cPtr, _("Missing parameter for command REMOVE."
+                                   " Use HELP for a list of commands.\n"));
+               return;
+       }
+
+       ptr = cmdArgs;
+       while (*ptr && *ptr != ' ' && *ptr != '\t')
+               ptr++;
+       *ptr = '\0';
+
+       WMRemoveUDObjectForKey(timeDB, cmdArgs);
+
+       for (i = 0; i < WMGetArrayItemCount(clientConnections); i++) {
+               cPtr = WMGetFromArray(clientConnections, i);
+               sendUpdateMessage(cPtr, cmdArgs, -1);
+       }
+}
+
+static void getTimeForUser(WMConnection * cPtr, char *cmdArgs)
+{
+       char *ptr;
+       int time;
+
+       if (cmdArgs[0] == '\0') {
+               sendMessage(cPtr, _("Missing parameter for command GET." " Use HELP for a list of commands.\n"));
+               return;
+       }
+
+       ptr = cmdArgs;
+       while (*ptr && *ptr != ' ' && *ptr != '\t')
+               ptr++;
+       *ptr = '\0';
+
+       if (WMGetUDObjectForKey(timeDB, cmdArgs) != NULL)
+               time = WMGetUDIntegerForKey(timeDB, cmdArgs);
+       else
+               time = -1;
+
+       sendUpdateMessage(cPtr, cmdArgs, time);
+}
+
+static void handleConnection(WMConnection * cPtr)
+{
+       char *command, *ptr, *cmdArgs, *buffer;
+       WMArray *commands;
+       int i;
+
+       commands = getAvailableMessages(cPtr);
+       if (!commands)
+               return;
+
+       for (i = 0; i < WMGetArrayItemCount(commands); i++) {
+               command = WMGetFromArray(commands, i);
+               while (*command && (*command == ' ' || *command == '\t'))
+                       command++;
+               ptr = command;
+               while (*ptr && *ptr != ' ' && *ptr != '\t')
+                       ptr++;
+               if (*ptr) {
+                       *ptr = '\0';
+                       ptr++;
+               }
+               while (*ptr && (*ptr == ' ' || *ptr == '\t'))
+                       ptr++;
+
+               cmdArgs = ptr;
+
+               fprintf(stderr, "Command: '%s', args: '%s'\n", command, cmdArgs);
+
+               if (strcasecmp(command, "quit") == 0 || strcasecmp(command, "exit") == 0) {
+                       sendMessage(cPtr, "Bye\n");
+                       WMCloseConnection(cPtr);
+                       enqueueConnectionForRemoval(cPtr);
+                       WMFreeArray(commands);
+                       return;
+               } else if (strcasecmp(command, "id") == 0) {
+                       showId(cPtr);
+               } else if (strcasecmp(command, "help") == 0) {
+                       showHelp(cPtr);
+               } else if (strcasecmp(command, "list") == 0) {
+                       listUsers(cPtr);
+               } else if (strcasecmp(command, "set") == 0) {
+                       setTimeForUser(cPtr, cmdArgs);
+               } else if (strcasecmp(command, "add") == 0) {
+                       addTimeToUser(cPtr, cmdArgs);
+               } else if (strcasecmp(command, "sub") == 0) {
+                       subTimeFromUser(cPtr, cmdArgs);
+               } else if (strcasecmp(command, "remove") == 0) {
+                       removeTimeForUser(cPtr, cmdArgs);
+               } else if (strcasecmp(command, "get") == 0) {
+                       getTimeForUser(cPtr, cmdArgs);
+               } else {
+                       buffer = wmalloc(strlen(command) + 100);
+                       sprintf(buffer, _("Unknown command '%s'. Try HELP for" " a list of commands.\n"), command);
+                       sendMessage(cPtr, buffer);
+                       wfree(buffer);
+               }
+       }
+
+       WMFreeArray(commands);
+}
+
+static Bool isAllowedToConnect(WMConnection * cPtr)
+{
+       WMHost *hPtr;
+       int i;
+
+       if (allowedHostList == NULL)
+               return True;    /* No list. Allow all by default */
+
+       hPtr = WMGetHostWithAddress(WMGetConnectionAddress(cPtr));
+       for (i = 0; i < WMGetArrayItemCount(allowedHostList); i++) {
+               if (WMIsHostEqualToHost(hPtr, WMGetFromArray(allowedHostList, i))) {
+                       WMReleaseHost(hPtr);
+                       return True;
+               }
+       }
+
+       WMReleaseHost(hPtr);
+
+       return False;
+}
+
+static void didReceiveInput(ConnectionDelegate * self, WMConnection * cPtr)
+{
+       if (cPtr == serverPtr) {
+               WMConnection *newPtr = WMAcceptConnection(cPtr);
+
+               if (newPtr) {
+                       if (isAllowedToConnect(newPtr)) {
+                               WMSetConnectionDelegate(newPtr, &socketDelegate);
+                               WMSetConnectionSendTimeout(newPtr, 120);
+                               WMAddToArray(clientConnections, newPtr);
+                       } else {
+                               sendMessage(newPtr, "Sorry, you are not allowed to connect.\n");
+                               WMDestroyConnection(newPtr);
+                       }
+               }
+       } else {
+               /* Data arriving on an already-connected socket */
+               handleConnection(cPtr);
+       }
+}
+
+static void connectionDidTimeout(ConnectionDelegate * self, WMConnection * cPtr)
+{
+       WMHost *hPtr;
+
+       if (cPtr == serverPtr) {
+               wfatal(_("The server listening socket did timeout. Exiting."));
+               exit(1);
+       }
+
+       hPtr = WMGetHostWithAddress(WMGetConnectionAddress(cPtr));
+       wwarning(_("Connection with %s did timeout. Closing connection."), WMGetHostName(hPtr));
+       WMReleaseHost(hPtr);
+
+       enqueueConnectionForRemoval(cPtr);
+}
+
+static void connectionDidDie(ConnectionDelegate * self, WMConnection * cPtr)
+{
+       if (cPtr == serverPtr) {
+               /* trouble. server listening port itself died!!! */
+               wfatal(_("The server listening socket died. Exiting."));
+               exit(1);
+       }
+
+       enqueueConnectionForRemoval(cPtr);
+}
+
+static void removeConnection(void *observer, WMNotification * notification)
+{
+       WMConnection *cPtr = (WMConnection *) WMGetNotificationObject(notification);
+       WMData *data;
+
+       WMRemoveFromArray(clientConnections, cPtr);
+       if ((data = (WMData *) WMGetConnectionClientData(cPtr)) != NULL)
+               WMReleaseData(data);
+       WMDestroyConnection(cPtr);
+}
+
+static void updatedDomain(void *observer, WMNotification * notification)
+{
+       wmessage("defaults domain file changed on disk. synchronizing.");
+}
+
+#if 0
+static Bool isDifferent(char *str1, char *str2)
+{
+       if ((!str1 && !str2) || (str1 && str2 && strcmp(str1, str2) == 0))
+               return False;
+
+       return True;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+       int i;
+
+       wsetabort(wAbort);
+
+       WMInitializeApplication("server", &argc, argv);
+
+       if (argc > 1) {
+               for (i = 1; i < argc; i++) {
+                       if (strcmp(argv[i], "--help") == 0) {
+                               printHelp(argv[0]);
+                               exit(0);
+                       } else if (strcmp(argv[i], "--listen") == 0) {
+                               char *p;
+
+                               if ((p = strchr(argv[++i], ':')) != NULL) {
+                                       *p = 0;
+                                       ServerAddress = wstrdup(argv[i]);
+                                       ServerPort = wstrdup(p + 1);
+                                       *p = ':';
+                                       if (ServerAddress[0] == 0) {
+                                               wfree(ServerAddress);
+                                               ServerAddress = NULL;
+                                       }
+                                       if (ServerPort[0] == 0) {
+                                               wfree(ServerPort);
+                                               ServerPort = "34567";
+                                       }
+                               } else if (argv[i][0] != 0) {
+                                       ServerPort = argv[i];
+                               }
+                       } else if (strcmp(argv[i], "--allow") == 0) {
+                               char *p, *ptr;
+                               int done;
+                               WMHost *hPtr;
+
+                               ptr = argv[++i];
+                               done = 0;
+                               while (!done) {
+                                       if ((p = strchr(ptr, ',')) != NULL) {
+                                               *p = 0;
+                                       }
+                                       if (*ptr != 0) {
+                                               hPtr = WMGetHostWithName(ptr);
+                                               if (hPtr) {
+                                                       if (!allowedHostList)
+                                                               allowedHostList = WMCreateArray(4);
+                                                       WMAddToArray(allowedHostList, hPtr);
+                                               } else {
+                                                       wwarning(_("Unknown host '%s'. Ignored."), ptr);
+                                               }
+                                       }
+
+                                       if (p != NULL) {
+                                               *p = ',';
+                                               ptr = p + 1;
+                                       } else {
+                                               done = 1;
+                                       }
+                               }
+                       } else {
+                               printf(_("%s: invalid argument '%s'\n"), argv[0], argv[i]);
+                               printf(_("Try '%s --help' for more information\n"), argv[0]);
+                               exit(1);
+                       }
+               }
+       }
+
+       timeDB = WMGetDefaultsFromPath("./UserTime.plist");
+       WMAddNotificationObserver(updatedDomain, NULL, WMUserDefaultsDidChangeNotification, NULL);
+
+       clientConnections = WMCreateArray(4);
+
+       /* A NULL ServerAddress means to listen on any address the host has.
+        * Else if ServerAddress points to a specific address (like "localhost",
+        * "host.domain.com" or "192.168.1.1"), then it will only listen on that
+        * interface and ignore incoming connections on the others. */
+       if (ServerAddress && strcasecmp(ServerAddress, "Any") == 0)
+               ServerAddress = NULL;
+       if (ServerPort == NULL)
+               ServerPort = "34567";
+
+       printf("Server will listen on '%s:%s'\n", ServerAddress ? ServerAddress : "Any", ServerPort);
+       printf("This server will allow connections from:");
+       if (allowedHostList) {
+               int i;
+               char *hName;
+
+               for (i = 0; i < WMGetArrayItemCount(allowedHostList); i++) {
+                       hName = WMGetHostName(WMGetFromArray(allowedHostList, i));
+                       printf("%s'%s'", i == 0 ? " " : ", ", hName);
+               }
+               printf(".\n");
+       } else {
+               printf(" any host.\n");
+       }
+
+       serverPtr = WMCreateConnectionAsServerAtAddress(ServerAddress, ServerPort, NULL);
+
+       if (!serverPtr) {
+               wfatal("could not create server on `%s:%s`. Exiting.",
+                      ServerAddress ? ServerAddress : "localhost", ServerPort);
+               exit(1);
+       }
+
+       WMSetConnectionDelegate(serverPtr, &socketDelegate);
+
+       WMAddNotificationObserver(removeConnection, NULL, SEConnectionShouldBeRemovedNotification, NULL);
+
+       while (1) {
+               /* The ASAP notification queue is called at the end of WHandleEvents()
+                * There's where died connections we get while running through
+                * WHandleEvents() get removed. */
+               WHandleEvents();
+       }
+
+       return 0;
+}