/* * WINGs server.c: example how to create a network server using WMConnection * * Copyright (c) 2001-2003 Dan Pascu * */ #include #include #include #include #include #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 \t- return time left (in minutes) " "for user with id \n")); enqueueMessage(cPtr, _("SET