From 0b624384cd52be20e61284551d832b499d7b7707 Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sat, 14 Apr 2012 12:56:48 +0100 Subject: Imported Upstream version 2.1.8.20120216 --- csocketopen.c | 2641 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2641 insertions(+) create mode 100644 csocketopen.c (limited to 'csocketopen.c') diff --git a/csocketopen.c b/csocketopen.c new file mode 100644 index 0000000..caaecfc --- /dev/null +++ b/csocketopen.c @@ -0,0 +1,2641 @@ +#include "stdafx.h" +#include "csocket.h" +#include "csocketevents.h" +#include "cphidgetlist.h" +#include "cphidgetmanager.h" +#include "cphidgetdictionary.h" +#ifdef USE_ZEROCONF +#include "zeroconf.h" +#endif + +/* +* Phidget WebService Protocol version history +* 1.0 - Initial version +* +* 1.0.1 +* -first version to be enforced +* -we changed around the device id numbers, so old webservice won't be able to talk to new +* +* 1.0.2 +* -Authorization is asynchronous, so we had to add Tags and now it's not compatible with old webservice +* -Doesn't match the old version checking! So could be ugly for users, get an unexpected error rather then a version error +* -Sends out all initial data, so it's just like opening locally +* -supports interfacekit Raw sensor value +* -supports labels on remoteIP managers +* +* 1.0.3 +* -supports servoType and setServoParameters for PhidgetServo and PhidgetAdvancedServo +* +* 1.0.4 +* -fixed RFID initialization - wasn't getting tag events if tag is on reader before open +* -fixed RFID sometimes not attaching in Flash +* +* 1.0.5 +* -added dataRate for InterfaceKit +* +* 1.0.6 +* -added brightness for TextLCD +* -support PhidgetSpatial +* -support PhidgetIR +* -1047 support (enable, index) +* +* 1.0.7 +* -1045 support +* -1011 support +* -1204 support +* -explicitely respond to report messages on Windows, to force an immediate ACK, and circumvent Windows delayed ACKs +* +* 1.0.8 +* -stopped sending explicit ACK - caused trouble on slower links +* -support for 1065, 1002, 1040, 1046, 1056 +* -support for error events +* +* 1.0.9 +* -support for openLabelRemote and openLabelRemoteIP +*/ +const char *ws_protocol_ver = "1.0.9"; + +/*void DNSServiceResolve_CallBack( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context);*/ + +typedef struct _CServerInfo +{ + CPhidgetSocketClientHandle server; + CPhidgetListHandle phidgets; + CPhidgetManagerListHandle managers; + CPhidgetDictionaryListHandle dictionaries; +} CServerInfo, *CServerInfoHandle; + +typedef struct _CServerList +{ + struct _CServerList *next; + CServerInfoHandle serverInfo; +} CServerList, *CServerListHandle; + +CThread_func_return_t CentralRemoteThreadFunction(CThread_func_arg_t arg); +static CThread CentralRemoteThread; +CThread_mutex_t CentralRemoteThreadLock; +int CentralRemoteThreadLockInitialized = PFALSE; + +static int connectionID = 0; + +// list of connected servers +CServerListHandle servers = NULL; +/* Protects servers */ +int serverLockInitialized = PFALSE; +CThread_mutex_t serverLock; +CThread_mutex_t serverLockLock; + +CPhidgetRemoteListHandle zeroconfServers = NULL; +CPhidgetListHandle zeroconfPhidgets = NULL; +CPhidgetSBCListHandle zeroconfSBCs = NULL; +/* Protects zeroconf lists */ +int zeroconfListLockInitialized = PFALSE; +CThread_mutex_t zeroconfServersLock; +CThread_mutex_t zeroconfPhidgetsLock; +CThread_mutex_t zeroconfSBCsLock; +CThread_mutex_t zeroconfInitLock; + +//Lists of objects that we have called openRemote or openRemoteIP on +//These may or may not actually be attached or connected +CPhidgetListHandle activeRemotePhidgets = NULL; +CPhidgetManagerListHandle activeRemoteManagers = NULL; +CPhidgetSBCManagerListHandle activeSBCManagers = NULL; +CPhidgetDictionaryListHandle activeRemoteDictionaries = NULL; +/* Protects the lists */ +int activeRemoteLocksInitialized = PFALSE; +CThread_mutex_t activeRemotePhidgetsLock; +CThread_mutex_t activeRemoteManagersLock; +CThread_mutex_t activeSBCManagersLock; +CThread_mutex_t activeRemoteDictionariesLock; + +int NetworkInitialized = PFALSE; + +int inErrorEvent = PFALSE; + +int closeServer(CServerInfoHandle server, unsigned char force); + + + +#ifdef _WINDOWS +int start_WSA_server() +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD( 2, 2 ); + + LOG(PHIDGET_LOG_INFO, "Starting WSA..."); + + err = WSAStartup( wVersionRequested, &wsaData ); + if ( err != 0 ) { + LOG(PHIDGET_LOG_ERROR,"Cannot start WSA"); + return EPHIDGET_NETWORK; + } + LOG(PHIDGET_LOG_INFO, "Started WSA Successfully"); + return EPHIDGET_OK; +} +#endif + +int CServerInfo_areEqual(void *arg1, void *arg2) +{ + CServerInfoHandle server1 = (CServerInfoHandle)arg1; + CServerInfoHandle server2 = (CServerInfoHandle)arg2; + + LOG(PHIDGET_LOG_VERBOSE, "In CServerInfo_areEqual, comparing: 0x%x and 0x%x", arg1, arg2); + + if(!server1 || !server2 || !server1->server || !server2->server) + return PFALSE; + + return CPhidgetSocketClient_areEqual(server1->server, server2->server); +} + +void CServerInfo_free(void *arg) +{ + CServerInfoHandle server = (CServerInfoHandle)arg; + LOG(PHIDGET_LOG_VERBOSE, "Freeing ServerInfo: 0x%x", arg); + + if(!server) + return; + if(server->server) + CPhidgetSocketClient_free(server->server); server->server = NULL; + //CList_emptyList((CListHandle *)&server->listen_ids, PFALSE, NULL); + + //empty the lists but don't free their objects + CList_emptyList((CListHandle *)&server->phidgets, PFALSE, NULL); + CList_emptyList((CListHandle *)&server->managers, PFALSE, NULL); + CList_emptyList((CListHandle *)&server->dictionaries, PFALSE, NULL); + + free(server); server = NULL; + return; +} + +typedef enum +{ + PHIDGET, + MANAGER, + DICTIONARY +} ListElementType; + +static int addToServerInfoList(CServerInfoHandle newServerInfo, void *element, ListElementType type) +{ + switch(type) + { + case PHIDGET: + LOG(PHIDGET_LOG_VERBOSE, "Adding Phidget (0x%x) to server (0x%x)", element, newServerInfo); + return CList_addToList((CListHandle *)&newServerInfo->phidgets, element, CPhidgetHandle_areEqual); + case MANAGER: + LOG(PHIDGET_LOG_VERBOSE, "Adding Manager (0x%x) to server (0x%x)", element, newServerInfo); + return CList_addToList((CListHandle *)&newServerInfo->managers, element, CPhidgetManager_areEqual); + case DICTIONARY: + LOG(PHIDGET_LOG_VERBOSE, "Adding Dictionary (0x%x) to server (0x%x)", element, newServerInfo); + return CList_addToList((CListHandle *)&newServerInfo->dictionaries, element, CPhidgetDictionary_areEqual); + } + return EPHIDGET_UNEXPECTED; +} + +static int removeFromServerInfoList(CServerInfoHandle newServerInfo, void *element, ListElementType type) +{ + switch(type) + { + case PHIDGET: + LOG(PHIDGET_LOG_VERBOSE, "Removing Phidget (0x%x) from server (0x%x)", element, newServerInfo); + return CList_removeFromList((CListHandle *)&newServerInfo->phidgets, element, CPhidgetHandle_areEqual, PFALSE, NULL); + case MANAGER: + LOG(PHIDGET_LOG_VERBOSE, "Removing Manager (0x%x) from server (0x%x)", element, newServerInfo); + return CList_removeFromList((CListHandle *)&newServerInfo->managers, element, CPhidgetManager_areEqual, PFALSE, NULL); + case DICTIONARY: + LOG(PHIDGET_LOG_VERBOSE, "Removing Dictionary (0x%x) from server (0x%x)", element, newServerInfo); + return CList_removeFromList((CListHandle *)&newServerInfo->dictionaries, element, CPhidgetDictionary_areEqual, PFALSE, NULL); + } + return EPHIDGET_UNEXPECTED; +} + +int CCONV CPhidgetRemote_create(CPhidgetRemoteHandle *arg) +{ + CPhidgetRemoteHandle remote; + + LOG(PHIDGET_LOG_VERBOSE, "Creating a new PhidgetRemote..."); + + if(!(remote = malloc(sizeof(CPhidgetRemote)))) + return EPHIDGET_NOMEMORY; + ZEROMEM(remote, sizeof(CPhidgetRemote)); + + CThread_mutex_init(&remote->zeroconf_ref_lock); + remote->cancelSocket = INVALID_SOCKET; + + remote->uniqueConnectionID = connectionID++; + + *arg = remote; + LOG(PHIDGET_LOG_VERBOSE, "Created new PhidgetRemote: 0x%x",remote); + + return EPHIDGET_OK; +} + +void CCONV CPhidgetRemote_free(void *arg) +{ + CPhidgetRemoteHandle remote = (CPhidgetRemoteHandle)arg; + + LOG(PHIDGET_LOG_VERBOSE, "Freeing PhidgetRemote: 0x%x",arg); + + if(!remote) return; + if(remote->requested_port) free(remote->requested_port); remote->requested_port=NULL; + if(remote->requested_address) free(remote->requested_address); remote->requested_address=NULL; + if(remote->requested_serverID) free(remote->requested_serverID); remote->requested_serverID=NULL; + if(remote->password) free(remote->password); remote->password=NULL; + + if(remote->zeroconf_name) free(remote->zeroconf_name); remote->zeroconf_name=NULL; + if(remote->zeroconf_domain) free(remote->zeroconf_domain); remote->zeroconf_domain=NULL; + if(remote->zeroconf_type) free(remote->zeroconf_type); remote->zeroconf_type=NULL; + if(remote->zeroconf_host) free(remote->zeroconf_host); remote->zeroconf_host=NULL; + if(remote->zeroconf_port) free(remote->zeroconf_port); remote->zeroconf_port=NULL; + if(remote->zeroconf_server_id) free(remote->zeroconf_server_id); remote->zeroconf_server_id=NULL; + + CPhidgetSocketClient_free(remote->server); + + CThread_mutex_destroy(&remote->zeroconf_ref_lock); + + free(remote); + + return; +} + +int CCONV CPhidgetRemote_areEqual(void *arg1, void *arg2) +{ + CPhidgetRemoteHandle remote1 = (CPhidgetRemoteHandle)arg1; + CPhidgetRemoteHandle remote2 = (CPhidgetRemoteHandle)arg2; + + LOG(PHIDGET_LOG_VERBOSE, "In CPhidgetRemote_areEqual, comparing: 0x%x and 0x%x", arg1, arg2); + + if(!remote1 || !remote2) + return PFALSE; + + if(remote1->zeroconf_name != NULL || remote2->zeroconf_name != NULL) + if((strcmp(remote1->zeroconf_name,remote2->zeroconf_name))) + return 0; + + if(remote1->zeroconf_domain != NULL || remote2->zeroconf_domain != NULL) + if((strcmp(remote1->zeroconf_domain,remote2->zeroconf_domain))) + return 0; + + if(remote1->zeroconf_type != NULL || remote2->zeroconf_type != NULL) + if((strcmp(remote1->zeroconf_type,remote2->zeroconf_type))) + return 0; + + if(remote1->requested_port != NULL || remote2->requested_port != NULL) + if((strcmp(remote1->requested_port,remote2->requested_port))) + return 0; + + if(remote1->requested_address != NULL || remote2->requested_address != NULL) + if((strcmp(remote1->requested_address,remote2->requested_address))) + return 0; + + if(remote1->requested_serverID != NULL || remote2->requested_serverID != NULL) + if((strcmp(remote1->requested_serverID,remote2->requested_serverID))) + return 0; + + return 1; +} + +int CCONV CPhidgetSocketClient_create(CPhidgetSocketClientHandle *arg) +{ + CPhidgetSocketClientHandle socket_client; + + LOG(PHIDGET_LOG_VERBOSE, "Creating a new PhidgetSocketClient..."); + + if(!(socket_client = malloc(sizeof(CPhidgetSocketClient)))) + return EPHIDGET_NOMEMORY; + ZEROMEM(socket_client, sizeof(CPhidgetSocketClient)); + + CThread_mutex_init(&socket_client->lock); + CThread_mutex_init(&socket_client->pdc_lock); + + CPhidget_clearStatusFlag(&socket_client->status, PHIDGETSOCKET_CONNECTED_FLAG, &socket_client->lock); + + *arg = socket_client; + LOG(PHIDGET_LOG_VERBOSE, "Created new PhidgetSocketClient: 0x%x",socket_client); + + return EPHIDGET_OK; +} + +void CCONV CPhidgetSocketClient_free(void *arg) +{ + CPhidgetSocketClientHandle socket_client = (CPhidgetSocketClientHandle)arg; + LOG(PHIDGET_LOG_VERBOSE, "Freeing PhidgetSocketClient: 0x%x",arg); + + if(!socket_client) return; + if(socket_client->port) free(socket_client->port); socket_client->port=NULL; + if(socket_client->address) free(socket_client->address); socket_client->address=NULL; + //if(socket_client->serverID) free(socket_client->serverID); socket_client->serverID=NULL; + if(socket_client->pdcs) free(socket_client->pdcs); socket_client->pdcs=NULL; + + CThread_mutex_destroy(&socket_client->lock); + CThread_mutex_destroy(&socket_client->pdc_lock); + + free(socket_client); + + return; +} + +int CCONV CPhidgetSocketClient_areEqual(void *arg1, void *arg2) +{ + CPhidgetSocketClientHandle socket1 = (CPhidgetSocketClientHandle)arg1; + CPhidgetSocketClientHandle socket2 = (CPhidgetSocketClientHandle)arg2; + LOG(PHIDGET_LOG_VERBOSE, "In CPhidgetSocketClient_areEqual, comparing: 0x%x and 0x%x", arg1, arg2); + + if(!socket1 || !socket2 || !socket1->address || !socket2->address || !socket1->port || !socket2->port) + return PFALSE; + + if(!(strcmp(socket1->address,socket2->address)) + && !(strcmp(socket1->port, socket2->port))) + return 1; + + return 0; +} + +void internal_async_network_error_handler(const char *error, void *ptr) +{ + CPhidgetHandle phid = (CPhidgetHandle)ptr; + if(phid && phid->fptrError) + { + LOG(PHIDGET_LOG_VERBOSE,"Got an async network error: %s", error); + phid->fptrError(phid, phid->fptrErrorptr, EEPHIDGET_NETWORK, error); + } + else + { + LOG(PHIDGET_LOG_WARNING,"Got an async network error: %s\n\tTip: Set up an error handler to catch this properly.", error); + } +} + +static int initialize_locks() +{ + LOG(PHIDGET_LOG_VERBOSE, "Initializing network locks..."); + if(!CentralRemoteThreadLockInitialized) + { + CThread_mutex_init(&CentralRemoteThreadLock); + CentralRemoteThreadLockInitialized = PTRUE; + } + if(!serverLockInitialized) + { + CThread_mutex_init(&serverLock); + CThread_mutex_init(&serverLockLock); + serverLockInitialized = PTRUE; + } + if(!zeroconfListLockInitialized) + { + CThread_mutex_init(&zeroconfServersLock); + CThread_mutex_init(&zeroconfPhidgetsLock); + CThread_mutex_init(&zeroconfSBCsLock); + CThread_mutex_init(&zeroconfInitLock); + zeroconfListLockInitialized = PTRUE; + } + if(!activeRemoteLocksInitialized) + { + CThread_mutex_init(&activeRemotePhidgetsLock); + CThread_mutex_init(&activeRemoteManagersLock); + CThread_mutex_init(&activeRemoteDictionariesLock); + CThread_mutex_init(&activeSBCManagersLock); + activeRemoteLocksInitialized = PTRUE; + } + return EPHIDGET_OK; +} + +int InitializeNetworking() +{ + int res; + const char *setpattern = "^/PSK/([a-zA-Z_0-9]*)/([a-zA-Z_0-9/.\\\\-]*)/([0-9]*)/([a-zA-Z_0-9]*)/?([a-zA-Z_0-9]*)/?([a-zA-Z_0-9]*)$"; + const char *managerpattern = "^/PSK/List/([a-zA-Z_0-9]*)/([0-9]*)$"; + const char *managervalpattern = "^([a-zA-Z]*) Version=([0-9]*) ID=([0-9]*) Label=(.*)$"; + + LOG(PHIDGET_LOG_VERBOSE, "Initializing Networking..."); + +#ifdef _WINDOWS + if (start_WSA_server()) + { + LOG(PHIDGET_LOG_WARNING,"Cannot start Windows Sockets"); + return EPHIDGET_NETWORK; + } +#endif + + if (!pdc_init()) { + LOG(PHIDGET_LOG_ERROR, "Error running pdc_init, networking couldn't start."); + return EPHIDGET_UNEXPECTED; + } + + if ((res = regcomp(&phidgetsetex, setpattern, REG_EXTENDED)) != 0) { + LOG_STDERR(PHIDGET_LOG_CRITICAL,"set command pattern compilation error %d", res); + abort(); + } + if ((res = regcomp(&managerex, managerpattern, REG_EXTENDED)) != 0) { + LOG_STDERR(PHIDGET_LOG_CRITICAL,"set command pattern compilation error %d", res); + abort(); + } + if ((res = regcomp(&managervalex, managervalpattern, REG_EXTENDED)) != 0) { + LOG_STDERR(PHIDGET_LOG_CRITICAL,"set command pattern compilation error %d", res); + abort(); + } + + NetworkInitialized = PTRUE; + LOG(PHIDGET_LOG_VERBOSE, "Networking initialized"); + + return EPHIDGET_OK; +} + +void cleanup_after_socket(void *ptr) +{ + CPhidgetSocketClientHandle serverInfo = ptr; + CServerList *travServers; + CServerInfoHandle foundServer = NULL; + CPhidgetListHandle travPhidgets; + CPhidgetDictionaryListHandle travDicts; + CPhidgetManagerListHandle travManagers; + + CPhidgetListHandle detachEvents = NULL; + CPhidgetListHandle disconnectEvents = NULL; + + LOG(PHIDGET_LOG_VERBOSE, "Cleaning up after socket: 0x%x",ptr); + + //wait for the threads to be done + while(serverInfo->auth_thread.thread_status == TRUE) + { + SLEEP(10); + } + while(serverInfo->auth_error_thread.thread_status == TRUE) + { + SLEEP(10); + } + + /* For each Phidget associated with this server, send a detach event, and a disconnect event */ + // We then get rid of the socket object everywhere + // If this is called because we already closed all the connections to this socket, no events will be raised because + // all the handles are already nulled... + CThread_mutex_lock(&serverLock); + + for(travServers = servers; travServers; travServers = travServers->next) + { + if(travServers->serverInfo->server->socket == serverInfo->socket) + { + foundServer = travServers->serverInfo; + CPhidget_clearStatusFlag(&foundServer->server->status, PHIDGETSOCKET_CONNECTED_FLAG, &foundServer->server->lock); + for(travPhidgets = foundServer->phidgets; travPhidgets; travPhidgets = travPhidgets->next) + { + if(CPhidget_statusFlagIsSet(travPhidgets->phid->status, PHIDGET_ATTACHED_FLAG)) + { + CPhidget_clearStatusFlag(&travPhidgets->phid->status, PHIDGET_ATTACHED_FLAG, &travPhidgets->phid->lock); + CPhidget_setStatusFlag(&travPhidgets->phid->status, PHIDGET_DETACHING_FLAG, &travPhidgets->phid->lock); + if(travPhidgets->phid->fptrDetach) + CList_addToList((CListHandle *)&detachEvents, travPhidgets->phid, CPhidget_areEqual); + } + + CPhidget_clearStatusFlag(&travPhidgets->phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &travPhidgets->phid->lock); + if(travPhidgets->phid->fptrServerDisconnect) + CList_addToList((CListHandle *)&disconnectEvents, travPhidgets->phid, CPhidgetHandle_areEqual); + } + for(travDicts = foundServer->dictionaries; travDicts; travDicts = travDicts->next) + { + CPhidget_clearStatusFlag(&travDicts->dict->status, PHIDGET_ATTACHED_FLAG, &travDicts->dict->lock); + + CPhidget_clearStatusFlag(&travDicts->dict->status, PHIDGET_SERVER_CONNECTED_FLAG, &travDicts->dict->lock); + if(travDicts->dict->fptrServerDisconnect) + CList_addToList((CListHandle *)&disconnectEvents, travDicts->dict, CPhidgetHandle_areEqual); + } + for(travManagers = foundServer->managers; travManagers; travManagers = travManagers->next) + { + CPhidget_clearStatusFlag(&travManagers->phidm->status, PHIDGET_ATTACHED_FLAG, &travManagers->phidm->lock); + + CPhidget_clearStatusFlag(&travManagers->phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &travManagers->phidm->lock); + if(travManagers->phidm->fptrServerDisconnect) + CList_addToList((CListHandle *)&disconnectEvents, travManagers->phidm, CPhidgetHandle_areEqual); + } + break; + } + } + + for(travPhidgets = disconnectEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + travPhidgets->phid->fptrServerDisconnect((CPhidgetHandle)travPhidgets->phid, travPhidgets->phid->fptrServerDisconnectptr); + //internal_async_network_error_handler("The Network Connection has been Closed", travPhidgets->phid); + } + for(travPhidgets = detachEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + travPhidgets->phid->fptrDetach((CPhidgetHandle)travPhidgets->phid, travPhidgets->phid->fptrDetachptr); + CPhidget_clearStatusFlag(&travPhidgets->phid->status, PHIDGET_DETACHING_FLAG, &travPhidgets->phid->lock); + } + + CList_emptyList((CListHandle *)&detachEvents, FALSE, NULL); + CList_emptyList((CListHandle *)&disconnectEvents, FALSE, NULL); + + if(foundServer) + { + //need to null all servers in list + //do it herer so detach/disconnect handlers can call getAddress, etc. + for(travPhidgets = foundServer->phidgets; travPhidgets; travPhidgets = travPhidgets->next) + { + travPhidgets->phid->networkInfo->server = NULL; + } + for(travDicts = foundServer->dictionaries; travDicts; travDicts = travDicts->next) + { + travDicts->dict->networkInfo->server = NULL; + } + for(travManagers = foundServer->managers; travManagers; travManagers = travManagers->next) + { + travManagers->phidm->networkInfo->server = NULL; + } + + //then remove from server list + CList_removeFromList((CListHandle *)&servers, foundServer, CServerInfo_areEqual, PTRUE, CServerInfo_free); + } + CThread_mutex_unlock(&serverLock); +} + +int setupHeartbeat(CPhidgetSocketClientHandle server, pdc_listen_id_t *id) +{ + char errdesc[1024]; + char listenKey[1024]; + + struct sockaddr_storage name; + char addr[200], *a; + socklen_t namelen = sizeof(name); + int port, e; + + char key[1024], val[1024]; + + TESTPTR(server) + LOG(PHIDGET_LOG_VERBOSE, "Setting up heartbeat: 0x%x...",server); + + if(getsockname(server->socket, (struct sockaddr *)&name, &namelen) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getsockname: %s", strerror(errno)); + return EPHIDGET_UNEXPECTED; + } + if((e = getnameinfo((struct sockaddr *)&name, namelen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getnameinfo: %s", gai_strerror(e)); + return EPHIDGET_UNEXPECTED; + } + port = (int)((struct sockaddr_in *)&name)->sin_port; + escape(addr, strlen(addr), &a); + + LOG(PHIDGET_LOG_VERBOSE, "Heartbeat addr/port: %s/%d",addr,port); + + //listen for heartbeat events + snprintf(listenKey, sizeof(listenKey), "/PCK/Heartbeat/%s/%d", a, port); + CThread_mutex_lock(&server->pdc_lock); + if (!(*id = pdc_listen(server->pdcs, listenKey, network_heartbeat_event_handler, server, errdesc, sizeof (errdesc)))) + { + LOG(PHIDGET_LOG_ERROR,"pdc_listen: %s", errdesc); + CThread_mutex_unlock(&server->pdc_lock); + free(a); + return EPHIDGET_UNEXPECTED; + } + CThread_mutex_unlock(&server->pdc_lock); + + snprintf(key, sizeof(key), "/PCK/Heartbeat/%s/%d", a, port); + free(a); + snprintf(val, sizeof(val), "%d", server->heartbeatCount); + setTimeNow(&server->lastHeartbeatTime); + server->waitingForHeartbeat = PTRUE; + pdc_async_set(server->pdcs, key, val, (int)strlen(val), PTRUE, NULL, NULL); + + return EPHIDGET_OK; +} + +int setupKeysAndListeners_phidget(CPhidgetHandle phid, pdc_listen_id_t *id) +{ + char errdesc[1024]; + char listenKey[1024]; + + struct sockaddr_storage name; + char addr[200], *a; + socklen_t namelen = sizeof(name); + int port; + int e; + + char key[1024], val[1024]; + + LOG(PHIDGET_LOG_VERBOSE, "Setting up keys and listeners for 0x%x",phid); + + TESTPTR(phid) + TESTPTR(phid->networkInfo) + TESTPTR(phid->networkInfo->server) + + //listen for everything to do with this specific phidget + if(phid->specificDevice == PHIDGETOPEN_SERIAL) + { + snprintf(listenKey, sizeof(listenKey), "^/PSK/%s/[a-zA-Z_0-9/.\\\\-]*/%d/", Phid_DeviceName[phid->deviceID], phid->serialNumber); + } + else if(phid->specificDevice == PHIDGETOPEN_LABEL) + { + char *l; + escape2(phid->label, strlen(phid->label), &l, PTRUE); //make sure the backslashes are escaped because this is a regex + snprintf(listenKey, sizeof(listenKey), "^/PSK/%s/%s/", Phid_DeviceName[phid->deviceID], l); + //LOG(PHIDGET_LOG_DEBUG, "Listening for: \"%s\"",listenKey); + } + else + { + snprintf(listenKey, sizeof(listenKey), "^/PSK/%s/", Phid_DeviceName[phid->deviceID]); + } + + CThread_mutex_lock(&phid->networkInfo->server->pdc_lock); + if (!(*id = pdc_listen(phid->networkInfo->server->pdcs, listenKey, network_phidget_event_handler, phid, errdesc, sizeof (errdesc)))) + { + LOG(PHIDGET_LOG_ERROR,"pdc_listen: %s", errdesc); + CThread_mutex_unlock(&phid->networkInfo->server->pdc_lock); + return EPHIDGET_UNEXPECTED; + } + CThread_mutex_unlock(&phid->networkInfo->server->pdc_lock); + //Open the remote device + //get socket info + + if(getsockname(phid->networkInfo->server->socket, (struct sockaddr *)&name, &namelen) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getsockname: %s", strerror(errno)); + return EPHIDGET_UNEXPECTED; + } + if((e = getnameinfo((struct sockaddr *)&name, namelen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getnameinfo: %s", gai_strerror(e)); + return EPHIDGET_UNEXPECTED; + } + port = (int)((struct sockaddr_in *)&name)->sin_port; + escape(addr, strlen(addr), &a); + + if(phid->specificDevice == PHIDGETOPEN_SERIAL) + { + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s/%d", a, phid->networkInfo->uniqueConnectionID, port, + Phid_DeviceName[phid->deviceID], phid->serialNumber); + } + else if(phid->specificDevice == PHIDGETOPEN_LABEL) + { + char *l; + escape(phid->label, strlen(phid->label), &l); + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s/-1/%s", a, phid->networkInfo->uniqueConnectionID, port, + Phid_DeviceName[phid->deviceID], l); + free(l); + } + else + { + snprintf(key, sizeof(key), "/PCK/Client/%s/%d%05d/%s", a, phid->networkInfo->uniqueConnectionID, port, + Phid_DeviceName[phid->deviceID]); + } + free(a); + snprintf(val, sizeof(val), "Open"); + pdc_async_set(phid->networkInfo->server->pdcs, key, val, (int)strlen(val), PTRUE, internal_async_network_error_handler, phid); + + return EPHIDGET_OK; +} + +int setupKeysAndListeners_manager(CPhidgetManagerHandle phidm, pdc_listen_id_t *id) +{ + char errdesc[1024]; + char listenKey[1024]; + + TESTPTR(phidm) + TESTPTR(phidm->networkInfo) + TESTPTR(phidm->networkInfo->server) + + //listen for everything to do with the PhidgetManager + snprintf(listenKey, sizeof(listenKey), "^/PSK/List/"); + + CThread_mutex_lock(&phidm->networkInfo->server->pdc_lock); + if (!(*id = pdc_listen(phidm->networkInfo->server->pdcs, listenKey, network_manager_event_handler, phidm, errdesc, sizeof (errdesc)))) + { + LOG(PHIDGET_LOG_ERROR,"pdc_listen: %s", errdesc); + CThread_mutex_unlock(&phidm->networkInfo->server->pdc_lock); + return EPHIDGET_UNEXPECTED; + } + CThread_mutex_unlock(&phidm->networkInfo->server->pdc_lock); + + return EPHIDGET_OK; +} + +typedef struct _AuthHandlerThreadData +{ + void *ptr; + void (*error)(const char *errdesc, void *arg); +} AuthHandlerThreadData, *AuthHandlerThreadDataHandle; + +//An auth succeeded so now we mark this as a connected server, send out connect events, etc. +CThread_func_return_t async_authorization_handler_thread(CThread_func_arg_t lpdwParam) +{ + AuthHandlerThreadDataHandle data = (AuthHandlerThreadDataHandle)lpdwParam; + + CPhidgetListHandle travPhidgets; + CPhidgetDictionaryListHandle travDicts; + CPhidgetManagerListHandle travManagers; + + CPhidgetListHandle connectEvents = NULL; + CPhidgetListHandle phidErrorEvents = NULL; + CPhidgetManagerListHandle managerErrorEvents = NULL; + + char errdesc[1024]; + CServerInfoHandle newServerInfo = (CServerInfoHandle)data->ptr; + + //make sure that we can cancel this thread +/*#ifndef _WINDOWS + int temp; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &temp); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &temp); +#endif*/ + + //CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + + //close was called, pdcs isn't valid any longer + if(!newServerInfo->server->pdcs) + { + newServerInfo->server->auth_thread.thread_status = FALSE; + + CThread_mutex_unlock(&serverLock); + return (CThread_func_return_t)0; + } + + CThread_mutex_lock(&newServerInfo->server->pdc_lock); + + //pdc_async_enable_periodic_reports(newServerInfo->server->pdcs, 10, NULL, NULL); + if(!pdc_enable_periodic_reports(newServerInfo->server->pdcs, 10, errdesc, sizeof(errdesc))) + { + LOG(PHIDGET_LOG_ERROR,"pdc_enable_periodic_reports: %s", errdesc); + + //this will call back to async_authorization_error_handler - the whole server connection is now bad + if(data->error) + { + data->error(errdesc, data->ptr); + } + + free(data); + CThread_mutex_unlock(&newServerInfo->server->pdc_lock); + + newServerInfo->server->auth_thread.thread_status = FALSE; + + CThread_mutex_unlock(&serverLock); + //CThread_mutex_unlock(&serverLockLock); + + return (CThread_func_return_t)0; + } + + free(data); + CThread_mutex_unlock(&newServerInfo->server->pdc_lock); + + //set connected + CPhidget_setStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTED_FLAG, &newServerInfo->server->lock); + CPhidget_clearStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTING_FLAG, &newServerInfo->server->lock); + + //Setup the heartbeat listener + setupHeartbeat(newServerInfo->server, &newServerInfo->server->heartbeat_listen_id); + + //now run through all phids, managers, dicts in the list and connect them + for(travPhidgets = newServerInfo->phidgets; travPhidgets; travPhidgets = travPhidgets->next) + { + CPhidget_setStatusFlag(&travPhidgets->phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &travPhidgets->phid->lock); + if(setupKeysAndListeners_phidget(travPhidgets->phid, &travPhidgets->phid->networkInfo->listen_id)) + { + if(travPhidgets->phid->fptrError) + CList_addToList((CListHandle *)&phidErrorEvents, travPhidgets->phid, CPhidgetHandle_areEqual); + CPhidget_clearStatusFlag(&travPhidgets->phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &travPhidgets->phid->lock); + //we have to remove this phid from the server list, + //and remove it's reference to the server, so that another connection attempt will be made. + travPhidgets->phid->networkInfo->server = NULL; + } + else + { + if(travPhidgets->phid->fptrServerConnect) + CList_addToList((CListHandle *)&connectEvents, travPhidgets->phid, CPhidgetHandle_areEqual); + } + } + for(travDicts = newServerInfo->dictionaries; travDicts; travDicts = travDicts->next) + { + CPhidget_setStatusFlag(&travDicts->dict->status, PHIDGET_SERVER_CONNECTED_FLAG, &travDicts->dict->lock); + CPhidget_setStatusFlag(&travDicts->dict->status, PHIDGET_ATTACHED_FLAG, &travDicts->dict->lock); + if(travDicts->dict->fptrServerConnect) + CList_addToList((CListHandle *)&connectEvents, travDicts->dict, CPhidgetHandle_areEqual); + } + for(travManagers = newServerInfo->managers; travManagers; travManagers = travManagers->next) + { + CPhidget_setStatusFlag(&travManagers->phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &travManagers->phidm->lock); + CPhidget_setStatusFlag(&travManagers->phidm->status, PHIDGET_ATTACHED_FLAG, &travManagers->phidm->lock); + if(setupKeysAndListeners_manager(travManagers->phidm, &travManagers->phidm->networkInfo->listen_id)) + { + if(travManagers->phidm->fptrError) + CList_addToList((CListHandle *)&managerErrorEvents, travManagers->phidm, CPhidgetHandle_areEqual); + CPhidget_clearStatusFlag(&travManagers->phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &travManagers->phidm->lock); + CPhidget_clearStatusFlag(&travManagers->phidm->status, PHIDGET_ATTACHED_FLAG, &travManagers->phidm->lock); + travManagers->phidm->networkInfo->server = NULL; + } + else + { + if(travManagers->phidm->fptrServerConnect) + CList_addToList((CListHandle *)&connectEvents, travManagers->phidm, CPhidgetHandle_areEqual); + } + } + + //do this here or it could interfere with close + for(travPhidgets = phidErrorEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + removeFromServerInfoList(newServerInfo, travPhidgets->phid, PHIDGET); + } + for(travManagers = managerErrorEvents; travManagers; travManagers = travManagers->next) + { + removeFromServerInfoList(newServerInfo, travManagers->phidm, MANAGER); + } + + //set this here so we can call close from events + newServerInfo->server->auth_thread.thread_status = FALSE; + + CThread_mutex_unlock(&serverLock); + //CThread_mutex_unlock(&serverLockLock); + + //send out events + for(travPhidgets = connectEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + travPhidgets->phid->fptrServerConnect((CPhidgetHandle)travPhidgets->phid, travPhidgets->phid->fptrServerConnectptr); + } + CList_emptyList((CListHandle *)connectEvents, PFALSE, NULL); + for(travPhidgets = phidErrorEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + travPhidgets->phid->fptrError((CPhidgetHandle)travPhidgets->phid, travPhidgets->phid->fptrErrorptr, EEPHIDGET_NETWORK, + "Error setting up phidget listeners from async_authorization_handler_thread"); + } + CList_emptyList((CListHandle *)phidErrorEvents, PFALSE, NULL); + for(travManagers = managerErrorEvents; travManagers; travManagers = travManagers->next) + { + travManagers->phidm->fptrError((CPhidgetManagerHandle)travManagers->phidm, travManagers->phidm->fptrErrorptr, EEPHIDGET_NETWORK, + "Error setting up manager listeners from async_authorization_handler_thread"); + } + CList_emptyList((CListHandle *)managerErrorEvents, PFALSE, NULL); + return (CThread_func_return_t)0; +} + +//this is only called when an auth succeeds +void async_authorization_handler(void *ptr, void (*error)(const char *errdesc, void *arg)) +{ + //need to start a thread because we can't call synchronous network functions like pdc_enable_periodic_reports from this callback + //They will just deadlock + CServerInfoHandle newServerInfo = (CServerInfoHandle)ptr; + AuthHandlerThreadDataHandle data; + data = malloc(sizeof(AuthHandlerThreadData)); + data->error = error; + data->ptr = ptr; + + //we do need to keep track of this thread so we can kill it on close if needed + // - it's associated with the server, and there should only be one running per server at a time! + if(newServerInfo->server->auth_thread.m_ThreadHandle) + { + newServerInfo->server->auth_thread.thread_status = FALSE; + CThread_join(&newServerInfo->server->auth_thread); + } + + newServerInfo->server->auth_thread.thread_status = TRUE; + CThread_create(&newServerInfo->server->auth_thread, async_authorization_handler_thread, data); +} + +typedef struct _AuthErrorHandlerThreadData +{ + char *error; + void *ptr; +} AuthErrorHandlerThreadData, *AuthErrorHandlerThreadDataHandle; + +CThread_func_return_t async_authorization_error_handler_thread(CThread_func_arg_t lpdwParam) +{ + AuthErrorHandlerThreadDataHandle data = (AuthErrorHandlerThreadDataHandle)lpdwParam; + + CPhidgetListHandle travPhidgets; + CPhidgetDictionaryListHandle travDicts; + CPhidgetManagerListHandle travManagers; + + CPhidgetListHandle errorEvents = NULL; + + CServerInfoHandle newServerInfo = (CServerInfoHandle)data->ptr; + + int errCode; + + const char *badPassStr = "Authentication Failed"; + const char *badVerStr = "Version Mismatch"; + + //make sure that we can cancel this thread +/*#ifndef _WINDOWS + int temp; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &temp); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &temp); +#endif*/ + + //CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + + if(strlen(data->error)>=strlen(badPassStr) && !strncmp(data->error, badPassStr, strlen(badPassStr))) + { + CPhidget_setStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_AUTHERROR_FLAG, &newServerInfo->server->lock); + errCode = EEPHIDGET_BADPASSWORD; + } + else if(strlen(data->error)>=strlen(badVerStr) && !strncmp(data->error, badVerStr, strlen(badVerStr))) + { + CPhidget_setStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTIONERROR_FLAG, &newServerInfo->server->lock); + errCode = EEPHIDGET_BADVERSION; + } + else + { + //we can't assume that newServerInfo is even valid + //CPhidget_setStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTIONERROR_FLAG, &newServerInfo->server->lock); + errCode = EEPHIDGET_NETWORK; + } + + //for each in server list, do the error handler - don't do a disconnect handler + //also remove the socket reference from the devices, and clears the lists in the serverInfo so that closeServer works + //PHIDGET_INERROREVENT_FLAG is set so that things don't break if we call close in the error event + //(don't want findActiveDevices to use these until after the error event fire) + for(travPhidgets = newServerInfo->phidgets; travPhidgets; travPhidgets = travPhidgets->next) + { + if(travPhidgets->phid->fptrError) + { + CPhidget_setStatusFlag(&travPhidgets->phid->status, PHIDGET_INERROREVENT_FLAG, &travPhidgets->phid->lock); + CList_addToList((CListHandle *)&errorEvents, travPhidgets->phid, CPhidgetHandle_areEqual); + } + travPhidgets->phid->networkInfo->server = NULL; + } + for(travDicts = newServerInfo->dictionaries; travDicts; travDicts = travDicts->next) + { + if(travDicts->dict->fptrError) + { + CPhidget_setStatusFlag(&travDicts->dict->status, PHIDGET_INERROREVENT_FLAG, &travDicts->dict->lock); + CList_addToList((CListHandle *)&errorEvents, travDicts->dict, CPhidgetHandle_areEqual); + } + travDicts->dict->networkInfo->server = NULL; + } + for(travManagers = newServerInfo->managers; travManagers; travManagers = travManagers->next) + { + if(travManagers->phidm->fptrError) + { + CPhidget_setStatusFlag(&travManagers->phidm->status, PHIDGET_INERROREVENT_FLAG, &travManagers->phidm->lock); + CList_addToList((CListHandle *)&errorEvents, travManagers->phidm, CPhidgetHandle_areEqual); + } + travManagers->phidm->networkInfo->server = NULL; + } + + CList_emptyList((CListHandle *)&newServerInfo->phidgets, PFALSE, NULL); + CList_emptyList((CListHandle *)&newServerInfo->managers, PFALSE, NULL); + CList_emptyList((CListHandle *)&newServerInfo->dictionaries, PFALSE, NULL); + + //clear this here, so we don't mess things up with findActiveDevices trying to attach these + CPhidget_clearStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTING_FLAG, &newServerInfo->server->lock); + + //do this here so we don't attempt a join in closeServer + newServerInfo->server->auth_error_thread.thread_status = FALSE; + + //now we call closserver explicitely - it doens't matter if they call close in the error event, as the server is already closed. + closeServer(newServerInfo, PFALSE); + + CThread_mutex_unlock(&serverLock); + //CThread_mutex_unlock(&serverLockLock); + + + for(travPhidgets = errorEvents; travPhidgets; travPhidgets = travPhidgets->next) + { + //they can block, close the phidgets, delete it, etc. in the error event and that shouldn't break anything. + //This thread will just exit right away, and that will be that... + travPhidgets->phid->fptrError((CPhidgetHandle)travPhidgets->phid, travPhidgets->phid->fptrErrorptr, errCode, data->error); + CPhidget_clearStatusFlag(&travPhidgets->phid->status, PHIDGET_INERROREVENT_FLAG, &travPhidgets->phid->lock); + } + CList_emptyList((CListHandle *)&errorEvents, PFALSE, NULL); + + free(data->error); + free(data); + + return (CThread_func_return_t)0; +} + +void async_authorization_error_handler(const char *error, void *ptr) +{ + //need to start a thread because we can't call synchronous netowrk functions from this callback + CServerInfoHandle newServerInfo = (CServerInfoHandle)ptr; + AuthErrorHandlerThreadDataHandle data; + data = malloc(sizeof(AuthErrorHandlerThreadData)); + data->error = strdup(error); + data->ptr = ptr; + + //we do need to keep track of this thread so we can kill it on close if needed + // - it's associated with the server, and there should only be one running per server at a time! + if(newServerInfo->server->auth_error_thread.m_ThreadHandle) + { + newServerInfo->server->auth_error_thread.thread_status = FALSE; + CThread_join(&newServerInfo->server->auth_error_thread); + } + + newServerInfo->server->auth_error_thread.thread_status = TRUE; + CThread_create(&newServerInfo->server->auth_error_thread, async_authorization_error_handler_thread, data); +} + +//This is only ever called once at a time +int connectToServer(CPhidgetRemoteHandle remoteInfo, char *errdesc, int errlen, void *list_element, ListElementType type) +{ +#ifdef ZEROCONF_LOOKUP + struct hostent * addr_lookup; + const char * addr_lookup_str; +#endif + int result = EPHIDGET_OK; + CServerInfoHandle foundServer = NULL; + CServerInfoHandle newServerInfo; + + LOG(PHIDGET_LOG_VERBOSE, "Connecting to server: 0x%x",remoteInfo); + + //Initialize the network if not already done + if(!NetworkInitialized) + if((result = InitializeNetworking())) + return result; + + //Creating a new server info object + if(!(newServerInfo = malloc(sizeof(CServerInfo)))) + return EPHIDGET_NOMEMORY; + ZEROMEM(newServerInfo, sizeof(CServerInfo)); + if((result = CPhidgetSocketClient_create(&newServerInfo->server))) + return result; + + //openRemoteIP + if(remoteInfo->requested_address != NULL) + { + LOG(PHIDGET_LOG_VERBOSE, "Connect with openRemoteIP"); + if(!(newServerInfo->server->address = strdup(remoteInfo->requested_address))) + return EPHIDGET_NOMEMORY; + if(!(newServerInfo->server->port = strdup(remoteInfo->requested_port))) + return EPHIDGET_NOMEMORY; + } + //openRemote +#ifdef USE_ZEROCONF + else //seems we've found an mDNS server we want to connect to + { + LOG(PHIDGET_LOG_VERBOSE, "Connect with openRemote, need to do hostname lookup..."); + if(getZeroconfHostPort(remoteInfo)) + return EPHIDGET_NETWORK; + if(!(newServerInfo->server->address = strdup(remoteInfo->zeroconf_host))) + return EPHIDGET_NOMEMORY; + if(!(newServerInfo->server->port = strdup(remoteInfo->zeroconf_port))) + return EPHIDGET_NOMEMORY; + } +#else + else + { + return EPHIDGET_INVALIDARG; + } +#endif + + LOG(PHIDGET_LOG_INFO, "Want to connect to server: %s:%s",newServerInfo->server->address,newServerInfo->server->port); + + //check to see if there is already a connection to this server + result = CList_findInList((CListHandle)servers, newServerInfo, CServerInfo_areEqual, (void **)&foundServer); + switch(result) + { + case EPHIDGET_OK: //Found + LOG(PHIDGET_LOG_VERBOSE, "Found an active connection to this server: 0x%x",foundServer); + remoteInfo->server = foundServer->server; + CServerInfo_free(newServerInfo); newServerInfo = NULL; + + //add device to list in serverInfo here + if((result = addToServerInfoList(foundServer, list_element, type))) + return result; + + //if the server is already connected, register listeners, and send out connect event, etc. + // - otherwise, they will be registered when it connects + if(CPhidget_statusFlagIsSet(remoteInfo->server->status, PHIDGETSOCKET_CONNECTED_FLAG)) + { + switch(type) + { + case PHIDGET: + { + CPhidgetHandle phid = (CPhidgetHandle)list_element; + CPhidget_setStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock); + if(setupKeysAndListeners_phidget(phid, &phid->networkInfo->listen_id)) + { + CPhidget_clearStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock); + if(phid->fptrError) + phid->fptrError((CPhidgetHandle)phid, phid->fptrErrorptr, EEPHIDGET_NETWORK, "Error setting up phidget listeners from connectToServer."); + removeFromServerInfoList(foundServer, list_element, type); + remoteInfo->server = NULL; + } + else + { + if(phid->fptrServerConnect) + phid->fptrServerConnect((CPhidgetHandle)phid, phid->fptrServerConnectptr); + } + } + break; + case MANAGER: + { + CPhidgetManagerHandle phidm = (CPhidgetManagerHandle)list_element; + CPhidget_setStatusFlag(&phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &phidm->lock); + CPhidget_setStatusFlag(&phidm->status, PHIDGET_ATTACHED_FLAG, &phidm->lock); + if(setupKeysAndListeners_manager(phidm, &phidm->networkInfo->listen_id)) + { + CPhidget_clearStatusFlag(&phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &phidm->lock); + CPhidget_clearStatusFlag(&phidm->status, PHIDGET_ATTACHED_FLAG, &phidm->lock); + if(phidm->fptrError) + phidm->fptrError((CPhidgetManagerHandle)phidm, phidm->fptrErrorptr, EEPHIDGET_NETWORK, "Error setting up manager listeners from connectToServer."); + removeFromServerInfoList(foundServer, list_element, type); + remoteInfo->server = NULL; + } + else + { + if(phidm->fptrServerConnect) + phidm->fptrServerConnect((CPhidgetManagerHandle)phidm, phidm->fptrServerConnectptr); + } + } + break; + case DICTIONARY: + { + CPhidgetDictionaryHandle dict = (CPhidgetDictionaryHandle)list_element; + CPhidget_setStatusFlag(&dict->status, PHIDGET_SERVER_CONNECTED_FLAG, &dict->lock); + CPhidget_setStatusFlag(&dict->status, PHIDGET_ATTACHED_FLAG, &dict->lock); + if(dict->fptrServerConnect) + dict->fptrServerConnect((CPhidgetDictionaryHandle)dict, dict->fptrServerConnectptr); + } + break; + } + } + break; + case EPHIDGET_NOTFOUND: //Not Found + + LOG(PHIDGET_LOG_VERBOSE, "We need to create a new connection..."); + //If the connection fails, this never even makes it into the server list, and the Phidget never sees it's server get initialized +#ifdef ZEROCONF_LOOKUP + + /* this will resolve to an IP address, including .local hostnames (for SBC, because it can't resolve .local hostnames on its own) */ + LOG(PHIDGET_LOG_VERBOSE, "Looking up address using internal mdns_gethostbyname: %s",newServerInfo->server->address); + addr_lookup = mdns_gethostbyname(newServerInfo->server->address); + if (addr_lookup == NULL) { + /* didn't work, just use address */ + addr_lookup_str = newServerInfo->server->address; + LOG(PHIDGET_LOG_VERBOSE, "Couldn't lookup address, using hostname"); + } + else + { + if (addr_lookup->h_addr_list[0] != NULL) + { + addr_lookup_str = inet_ntoa( *( struct in_addr*)( addr_lookup -> h_addr_list[0])); + LOG(PHIDGET_LOG_VERBOSE, "Found this address: %s",addr_lookup_str); + } + else + { + addr_lookup_str = newServerInfo->server->address; + LOG(PHIDGET_LOG_VERBOSE, "Couldn't lookup address, tring with hostname anyways..."); + } + } + + if (!stream_server_connect(addr_lookup_str, newServerInfo->server->port, &newServerInfo->server->socket, &remoteInfo->cancelSocket, errdesc, errlen)) + { +#else + if (!stream_server_connect(newServerInfo->server->address, newServerInfo->server->port, &newServerInfo->server->socket, &remoteInfo->cancelSocket, errdesc, errlen)) + { +#endif + LOG(PHIDGET_LOG_ERROR,"connect(%s:%s): %s", newServerInfo->server->address, newServerInfo->server->port, errdesc); + CServerInfo_free(newServerInfo); newServerInfo = NULL; + if(errno==ECANCELED) + return EPHIDGET_INTERRUPTED; + return EPHIDGET_NETWORK; + } + + LOG(PHIDGET_LOG_VERBOSE, "Connection was successfull."); + + if (!(newServerInfo->server->pdcs = pdc_session_alloc(newServerInfo->server->socket, pu_read, newServerInfo->server->socket, + pu_write, pu_close, newServerInfo->server, cleanup_after_socket))) { + fflush(stderr); + CServerInfo_free(newServerInfo); newServerInfo = NULL; + return EPHIDGET_NOTFOUND; + } + + //set authenticating state + CPhidget_setStatusFlag(&newServerInfo->server->status, PHIDGETSOCKET_CONNECTING_FLAG, &newServerInfo->server->lock); + + //set server for this device to new server + remoteInfo->server = newServerInfo->server; + + //add it to the list - note we are allowed to have connecting servers in the list! + if((result = CList_addToList((CListHandle *)&servers, newServerInfo, CServerInfo_areEqual))) + return result; + + //add device to list in serverInfo here + if((result = addToServerInfoList(newServerInfo, list_element, type))) + return result; + + //connection is made - start authorization - this should return as one of two callbacks - success or error + //TODO: have some sort of timeout for the connection to succeed or fail + pdc_async_authorize(newServerInfo->server->pdcs, ws_protocol_ver, + remoteInfo->password, async_authorization_handler, + async_authorization_error_handler, newServerInfo); + + //Start looking for a timeout + setTimeNow(&newServerInfo->server->lastHeartbeatTime); + newServerInfo->server->waitingForHeartbeat = PTRUE; + + break; + default: + return result; + } + + LOG(PHIDGET_LOG_VERBOSE, "returning from end of connectToServer with successfull result."); + return EPHIDGET_OK; +} + +int MonitorHeartbeats() +{ + CServerList *travServers; + CPhidgetSocketClientHandle server; + + struct sockaddr_storage name; + char addr[200], *a; + socklen_t namelen = sizeof(name); + int port, e; + + char key[1024], val[1024]; + + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + +start: + for(travServers = servers; travServers; travServers = travServers->next) + { + if(travServers->serverInfo && travServers->serverInfo->server) + { + server = travServers->serverInfo->server; + + if(server->waitingForHeartbeat && !server->runningEvent) + { + //if we've been waiting too long, then signal disconnect + double waitTime = timeSince(&server->lastHeartbeatTime); + //if we haven't recieved any heartbeats, set the timeout high (4*4 = 16 seconds) + //This is so that really slow connections will get through the auth stage + double avgPingTime = ((server->avgHeartbeatTimeCount > 0) ? (server->avgHeartbeatTime / server->avgHeartbeatTimeCount) : 4.0); + + //10 times the average ping time, or 2 seconds, whichever is larger + if(waitTime > (avgPingTime * 10) && waitTime > 2.0) + { + server->waitingForHeartbeat = PFALSE; + server->avgHeartbeatTime = 0; + server->avgHeartbeatTimeCount = 0; + //close the socket - this will filter detach events down the chain + closeServer(travServers->serverInfo, PTRUE); + //need to exit the loop because we removed the server from it! + goto start; + } + } + else + { + //new heartbeat every 2 seconds + if(timeSince(&server->lastHeartbeatTime) > 2.0) + { + if(getsockname(server->socket, (struct sockaddr *)&name, &namelen) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getsockname: %s", strerror(errno)); + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + return EPHIDGET_UNEXPECTED; + } + if((e = getnameinfo((struct sockaddr *)&name, namelen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) + { + LOG(PHIDGET_LOG_ERROR,"getnameinfo: %s", gai_strerror(e)); + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + return EPHIDGET_UNEXPECTED; + } + port = (int)((struct sockaddr_in *)&name)->sin_port; + escape(addr, strlen(addr), &a); + + snprintf(key, sizeof(key), "/PCK/Heartbeat/%s/%d", a, port); + free(a); + snprintf(val, sizeof(val), "%d", server->heartbeatCount); + server->waitingForHeartbeat = PTRUE; + setTimeNow(&server->lastHeartbeatTime); + pdc_async_set(server->pdcs, key, val, (int)strlen(val), PTRUE, NULL, NULL); + } + } + } + } + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + return EPHIDGET_OK; +} + +// This will try to connect unconnected openRemoteIP devices and find any zeroconf matches +int +FindActiveRemoteDevices() +{ + CPhidgetList *activePhidTrav = 0, *zeroconfPhidTrav; + CPhidgetManagerList *activeManagerTrav; + CPhidgetDictionaryList *activeDictTrav; + CPhidgetRemoteList *zeroconfServerTrav; + int result = 0; + char errdesc[1024]; + void *err_device = NULL; + + errdesc[0] = '\0'; + + //Zeroconf Phidgets + CThread_mutex_lock(&activeRemotePhidgetsLock); + CThread_mutex_lock(&zeroconfPhidgetsLock); + + for (zeroconfPhidTrav=zeroconfPhidgets; zeroconfPhidTrav; zeroconfPhidTrav = zeroconfPhidTrav->next) + { + //first look for specific serial numbers + for (activePhidTrav=activeRemotePhidgets; activePhidTrav; activePhidTrav = activePhidTrav->next) + { + if(!CPhidget_statusFlagIsSet(activePhidTrav->phid->status, PHIDGET_INERROREVENT_FLAG) + && activePhidTrav->phid->networkInfo->server == NULL + && ((activePhidTrav->phid->specificDevice == PHIDGETOPEN_SERIAL && (activePhidTrav->phid->serialNumber == zeroconfPhidTrav->phid->serialNumber)) + || (activePhidTrav->phid->specificDevice == PHIDGETOPEN_LABEL && !strncmp(activePhidTrav->phid->label, zeroconfPhidTrav->phid->label, MAX_LABEL_STORAGE-1))) + && activePhidTrav->phid->deviceID == zeroconfPhidTrav->phid->deviceID + && activePhidTrav->phid->networkInfo->requested_address==NULL + && (activePhidTrav->phid->networkInfo->requested_serverID == NULL + || !strcmp(activePhidTrav->phid->networkInfo->requested_serverID,zeroconfPhidTrav->phid->networkInfo->zeroconf_server_id))) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + + free(activePhidTrav->phid->networkInfo->zeroconf_name); activePhidTrav->phid->networkInfo->zeroconf_name = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_name); + free(activePhidTrav->phid->networkInfo->zeroconf_type); activePhidTrav->phid->networkInfo->zeroconf_type = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_type); + free(activePhidTrav->phid->networkInfo->zeroconf_domain); activePhidTrav->phid->networkInfo->zeroconf_domain = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_domain); + free(activePhidTrav->phid->networkInfo->zeroconf_server_id); activePhidTrav->phid->networkInfo->zeroconf_server_id = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_server_id); + + if((result = connectToServer(activePhidTrav->phid->networkInfo, errdesc, sizeof(errdesc), activePhidTrav->phid, PHIDGET)) != EPHIDGET_OK) + { + err_device = activePhidTrav->phid; + activePhidTrav->phid->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&zeroconfPhidgetsLock); + CThread_mutex_unlock(&activeRemotePhidgetsLock); + goto error_event; + } + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + goto next_zeroconf_phidget; + } + } + + //then take care of -1 (any) serial numbers + for (activePhidTrav=activeRemotePhidgets; activePhidTrav; activePhidTrav = activePhidTrav->next) + { + if(!CPhidget_statusFlagIsSet(activePhidTrav->phid->status, PHIDGET_INERROREVENT_FLAG) + && !activePhidTrav->phid->networkInfo->server + && CPhidget_areEqual(activePhidTrav->phid, zeroconfPhidTrav->phid) + && activePhidTrav->phid->networkInfo->requested_address==NULL + && (activePhidTrav->phid->networkInfo->requested_serverID == NULL + || !strcmp(activePhidTrav->phid->networkInfo->requested_serverID,zeroconfPhidTrav->phid->networkInfo->zeroconf_server_id))) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + + free(activePhidTrav->phid->networkInfo->zeroconf_name); activePhidTrav->phid->networkInfo->zeroconf_name = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_name); + free(activePhidTrav->phid->networkInfo->zeroconf_type); activePhidTrav->phid->networkInfo->zeroconf_type = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_type); + free(activePhidTrav->phid->networkInfo->zeroconf_domain); activePhidTrav->phid->networkInfo->zeroconf_domain = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_domain); + free(activePhidTrav->phid->networkInfo->zeroconf_server_id); activePhidTrav->phid->networkInfo->zeroconf_server_id = strdup(zeroconfPhidTrav->phid->networkInfo->zeroconf_server_id); + + if((result = connectToServer(activePhidTrav->phid->networkInfo, errdesc, sizeof(errdesc), activePhidTrav->phid, PHIDGET)) != EPHIDGET_OK) + { + err_device = activePhidTrav->phid; + activePhidTrav->phid->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&zeroconfPhidgetsLock); + CThread_mutex_unlock(&activeRemotePhidgetsLock); + goto error_event; + } + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + goto next_zeroconf_phidget; + } + } +next_zeroconf_phidget:; + } + + CThread_mutex_unlock(&zeroconfPhidgetsLock); + CThread_mutex_unlock(&activeRemotePhidgetsLock); + + + //Zeroconf Dictionaries + CThread_mutex_lock(&activeRemoteDictionariesLock); + CThread_mutex_lock(&zeroconfServersLock); + for (zeroconfServerTrav=zeroconfServers; zeroconfServerTrav; zeroconfServerTrav = zeroconfServerTrav->next) + { + for (activeDictTrav=activeRemoteDictionaries; activeDictTrav; activeDictTrav = activeDictTrav->next) + { + if(!CPhidget_statusFlagIsSet(activeDictTrav->dict->status, PHIDGET_INERROREVENT_FLAG) + && !activeDictTrav->dict->networkInfo->server + && activeDictTrav->dict->networkInfo->requested_address==NULL + && (activeDictTrav->dict->networkInfo->requested_serverID == NULL + || !strcmp(activeDictTrav->dict->networkInfo->requested_serverID,zeroconfServerTrav->networkInfo->zeroconf_name))) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + free(activeDictTrav->dict->networkInfo->zeroconf_name); activeDictTrav->dict->networkInfo->zeroconf_name = strdup(zeroconfServerTrav->networkInfo->zeroconf_name); + free(activeDictTrav->dict->networkInfo->zeroconf_type); activeDictTrav->dict->networkInfo->zeroconf_type = strdup(zeroconfServerTrav->networkInfo->zeroconf_type); + free(activeDictTrav->dict->networkInfo->zeroconf_domain); activeDictTrav->dict->networkInfo->zeroconf_domain = strdup(zeroconfServerTrav->networkInfo->zeroconf_domain); + free(activeDictTrav->dict->networkInfo->zeroconf_server_id); activeDictTrav->dict->networkInfo->zeroconf_server_id = strdup(zeroconfServerTrav->networkInfo->zeroconf_server_id); + if((result = connectToServer(activeDictTrav->dict->networkInfo, errdesc, sizeof(errdesc), activeDictTrav->dict, DICTIONARY)) != EPHIDGET_OK) + { + err_device = activeDictTrav->dict; + activeDictTrav->dict->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&zeroconfServersLock); + CThread_mutex_unlock(&activeRemoteDictionariesLock); + goto error_event; + } + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + } + } + } + CThread_mutex_unlock(&zeroconfServersLock); + CThread_mutex_unlock(&activeRemoteDictionariesLock); + + //IP Phidgets + CThread_mutex_lock(&activeRemotePhidgetsLock); + for (activePhidTrav=activeRemotePhidgets; activePhidTrav; activePhidTrav = activePhidTrav->next) + { + if(activePhidTrav->phid->networkInfo->requested_address!=NULL) + { + if(!CPhidget_statusFlagIsSet(activePhidTrav->phid->status, PHIDGET_INERROREVENT_FLAG) + && !activePhidTrav->phid->networkInfo->server) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + if((result = connectToServer(activePhidTrav->phid->networkInfo, errdesc, sizeof(errdesc), activePhidTrav->phid, PHIDGET)) != EPHIDGET_OK) + { + err_device = activePhidTrav->phid; + activePhidTrav->phid->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&activeRemotePhidgetsLock); + goto error_event; + } + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + } + } + } + CThread_mutex_unlock(&activeRemotePhidgetsLock); + + //IP Managers + CThread_mutex_lock(&activeRemoteManagersLock); + for (activeManagerTrav=activeRemoteManagers; activeManagerTrav; activeManagerTrav = activeManagerTrav->next) + { + if(activeManagerTrav->phidm->networkInfo->requested_address!=NULL) + { + if(!CPhidget_statusFlagIsSet(activeManagerTrav->phidm->status, PHIDGET_INERROREVENT_FLAG) + && !activeManagerTrav->phidm->networkInfo->server ) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + if((result = connectToServer(activeManagerTrav->phidm->networkInfo, errdesc, sizeof(errdesc), activeManagerTrav->phidm, MANAGER)) != EPHIDGET_OK) + { + err_device = activeManagerTrav->phidm; + activeManagerTrav->phidm->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&activeRemoteManagersLock); + goto error_event; + } + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + } + } + } + CThread_mutex_unlock(&activeRemoteManagersLock); + + //IP Dictionaries + CThread_mutex_lock(&activeRemoteDictionariesLock); + for (activeDictTrav=activeRemoteDictionaries; activeDictTrav; activeDictTrav = activeDictTrav->next) + { + if(activeDictTrav->dict->networkInfo->requested_address!=NULL) + { + if(!CPhidget_statusFlagIsSet(activeDictTrav->dict->status, PHIDGET_INERROREVENT_FLAG) + && !activeDictTrav->dict->networkInfo->server) + { + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + if((result = connectToServer(activeDictTrav->dict->networkInfo, errdesc, sizeof(errdesc), activeDictTrav->dict, DICTIONARY)) != EPHIDGET_OK) + { + err_device = activeDictTrav->dict; + activeDictTrav->dict->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + CThread_mutex_unlock(&activeRemoteDictionariesLock); + goto error_event; + } + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + } + } + } + CThread_mutex_unlock(&activeRemoteDictionariesLock); + + return result; + + //send out any error events that have pilled up... + //send them out here so we can close, open, etc. from within the error event +error_event: + //if the error did not originate in a pdc function, we'll fill in our own error description + if(errdesc[0] == '\0') + strncpy(errdesc, Phid_ErrorDescriptions[result], sizeof(errdesc)); + //no error event for interrupted - this means that a connect was cancelled by a close + if(result != EPHIDGET_INTERRUPTED) + { + inErrorEvent = PTRUE; + throw_error_event(err_device, errdesc, EEPHIDGET_NETWORK); + inErrorEvent = PFALSE; + } + return result; +} + +//shuts down the central remote thread - we should also make sure zeroconf gets shut down here if it's running +int JoinCentralRemoteThread() +{ + if(CentralRemoteThread.m_ThreadHandle && !CThread_is_my_thread(CentralRemoteThread) && !inErrorEvent) + { + CThread_join(&CentralRemoteThread); + CentralRemoteThread.m_ThreadHandle = 0; + } +#ifdef USE_ZEROCONF + if(!activeSBCManagers) + { + UninitializeZeroconf();//shut down zeroconf + } +#endif + return EPHIDGET_OK; +} + +int +StartCentralRemoteThread() +{ + CThread_mutex_lock(&CentralRemoteThreadLock); +#ifdef _WINDOWS + if (CentralRemoteThread.m_ThreadHandle) { + int threadStatus = 0; + int result = 0; + result = GetExitCodeThread(CentralRemoteThread.m_ThreadHandle, + (LPDWORD)&threadStatus); + if (result) { + if (threadStatus != STILL_ACTIVE) { + CloseHandle(CentralRemoteThread.m_ThreadHandle); + CentralRemoteThread.m_ThreadHandle = 0; + } + } + } +#endif + + if (!CentralRemoteThread.m_ThreadHandle || + CentralRemoteThread.thread_status == FALSE) + { + if (CThread_create(&CentralRemoteThread, CentralRemoteThreadFunction, 0)) + return EPHIDGET_UNEXPECTED; + CentralRemoteThread.thread_status = TRUE; + } + CThread_mutex_unlock(&CentralRemoteThreadLock); + return EPHIDGET_OK; +} + +CThread_func_return_t CentralRemoteThreadFunction(CThread_func_arg_t lpdwParam) +{ + initialize_locks(); + while(activeRemotePhidgets || activeRemoteManagers || activeRemoteDictionaries) { + FindActiveRemoteDevices(); //this looks for attached active devices and opens them + MonitorHeartbeats(); //sends out new heartbeats, detects when a server has gone down + SLEEP(250); + } + CentralRemoteThread.thread_status = FALSE; + return EPHIDGET_OK; +} + +int RegisterRemotePhidget(CPhidgetHandle phid) +{ + int result = EPHIDGET_OK; + + //clear all variables + phid->fptrClear((CPhidgetHandle)phid); + phid->initKeys = PUNK_INT; + + CThread_mutex_lock(&activeRemotePhidgetsLock); + + result = CList_addToList((CListHandle *)&activeRemotePhidgets, phid, CPhidgetHandle_areEqual); + + if (result) + { + CThread_mutex_unlock(&activeRemotePhidgetsLock); + return result; + } + CThread_mutex_unlock(&activeRemotePhidgetsLock); + + result = StartCentralRemoteThread(); + + return result; +} + +int RegisterRemoteManager(CPhidgetManagerHandle phidm) +{ + int result = EPHIDGET_OK; + + CThread_mutex_lock(&activeRemoteManagersLock); + + result = CList_addToList((CListHandle *)&activeRemoteManagers, phidm, CPhidgetHandle_areEqual); + + if (result) + { + CThread_mutex_unlock(&activeRemoteManagersLock); + return result; + } + + CThread_mutex_unlock(&activeRemoteManagersLock); + + result = StartCentralRemoteThread(); + + return result; +} + +int RegisterRemoteDictionary(CPhidgetDictionaryHandle dict) +{ + int result = EPHIDGET_OK; + + CThread_mutex_lock(&activeRemoteDictionariesLock); + + if ((result = CList_addToList((CListHandle *)&activeRemoteDictionaries, dict, CPhidgetHandle_areEqual)) != EPHIDGET_OK) + { + CThread_mutex_unlock(&activeRemoteDictionariesLock); + return result; + } + + CThread_mutex_unlock(&activeRemoteDictionariesLock); + + result = StartCentralRemoteThread(); + + return result; +} + +int RegisterSBCManager(CPhidgetSBCManagerHandle sbcm) +{ + int result = EPHIDGET_OK; + + CThread_mutex_lock(&activeSBCManagersLock); + + result = CList_addToList((CListHandle *)&activeSBCManagers, sbcm, CPhidgetHandle_areEqual); + + if (result) + { + CThread_mutex_unlock(&activeSBCManagersLock); + return result; + } + + CThread_mutex_unlock(&activeSBCManagersLock); + + result = StartCentralRemoteThread(); + + return result; +} + +// This will close a server connection (and cleanup the socket, etc) if there are no more +// Phidgets, Managers, or Dictionaries in it's lists +int closeServer(CServerInfoHandle server, unsigned char force) +{ + char errdesc[1024]; + void *pdcs = server->server->pdcs; + //no more references to this server + if(((!server->phidgets && !server->dictionaries && !server->managers) || force) && pdcs) + { + wait_pending(server->server->pdcs); + CThread_mutex_lock(&server->server->pdc_lock); + + //rather then calling quit - which can easily block, just close the socket! + if(pu_close(server->server->socket,errdesc,sizeof(errdesc))) + { + LOG(PHIDGET_LOG_ERROR,"pu_close: %s", errdesc); + } + + CThread_mutex_unlock(&server->server->pdc_lock); + //We need to wait for the read thread to return + //unlock serverLock so it can be claimed in the cleanup function + //don't want the cleanup function to free pdcs while we're waiting on it.. + //this is safe because we don't release serverLockLock, so the only thing that is able to claim serverLock is the cleanup function + server->server->pdcs = NULL; + + CThread_mutex_unlock(&serverLock); + + pdc_readthread_join(pdcs, NULL); + + CThread_mutex_lock(&serverLock); + + pdc_session_free(pdcs); + return EPHIDGET_CLOSED; + } + return EPHIDGET_OK; +} + +int disconnectRemoteObject(void *object, size_t objectListOffset, int(*compareObjects)(void *, void *)) +{ + int result = EPHIDGET_OK; + int closeRes; + CServerInfoHandle foundServer; + CServerInfo newServerInfo; + + CPhidgetHandle phid = object; + + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + + if(phid->networkInfo->server) + { + newServerInfo.server = phid->networkInfo->server; + + //check to see if there is already a connection to this server + result = CList_findInList((CListHandle)servers, &newServerInfo, CServerInfo_areEqual, (void **)&foundServer); + switch(result) + { + case EPHIDGET_OK: //Found + + if((result = CList_removeFromList((CListHandle *)((size_t)foundServer + objectListOffset), phid, compareObjects, PFALSE, NULL))) + { + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + + //stop reports for this Object + //async so it won't block if the connection is bad + if(phid->networkInfo->listen_id) + { + CThread_mutex_lock(&phid->networkInfo->server->pdc_lock); + pdc_ignore(foundServer->server->pdcs,phid->networkInfo->listen_id,NULL,0); + phid->networkInfo->listen_id = 0; + CThread_mutex_unlock(&phid->networkInfo->server->pdc_lock); + } + + //closes if there are no more references + closeRes = closeServer(foundServer, PFALSE); + phid->networkInfo->server = NULL; + + //Run error event for any async commands that didn't complete + if(closeRes != EPHIDGET_CLOSED && foundServer->server->pdcs) + cleanup_pending(foundServer->server->pdcs, object); + + break; + case EPHIDGET_NOTFOUND: //Not Found - That's ok, just means it's already closed + phid->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return EPHIDGET_OK; + default: + phid->networkInfo->server = NULL; + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + } + + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return EPHIDGET_OK; +} + +CThread_func_return_t DisconnectPhidgetThreadFunction(CThread_func_arg_t lpdwParam) +{ + CPhidgetHandle phid = (CPhidgetHandle)lpdwParam; + + CPhidget_clearStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock); + + if(phid->fptrServerDisconnect) + phid->fptrServerDisconnect((CPhidgetHandle)phid, phid->fptrServerDisconnectptr); + + disconnectRemoteObject(phid, offsetof(CServerInfo, phidgets), CPhidgetHandle_areEqual); + + return EPHIDGET_OK; +} + +int unregisterRemotePhidget(CPhidgetHandle phid) +{ + int result = EPHIDGET_OK; + + //cancel a pending connect + if(phid->networkInfo->cancelSocket != INVALID_SOCKET) + { + cancelConnect(phid->networkInfo->cancelSocket); + } +#ifdef USE_ZEROCONF + cancelPendingZeroconfLookups(phid->networkInfo); +#endif + + CThread_mutex_lock(&activeRemotePhidgetsLock); + + result = CList_removeFromList((CListHandle *)&activeRemotePhidgets, phid, CPhidgetHandle_areEqual, FALSE, NULL); + + if (result) + { + CThread_mutex_unlock(&activeRemotePhidgetsLock); + return result; + } + + CThread_mutex_unlock(&activeRemotePhidgetsLock); + + CPhidget_clearStatusFlag(&phid->status, PHIDGET_SERVER_CONNECTED_FLAG, &phid->lock); + CPhidget_clearStatusFlag(&phid->status, PHIDGET_ATTACHED_FLAG, &phid->lock); + + result = disconnectRemoteObject(phid, offsetof(CServerInfo, phidgets), CPhidgetHandle_areEqual); + + CPhidget_clearStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock); + + /* don't free this here - it may be referenced elsewhere! */ + CThread_mutex_lock(&phid->lock); + phid->networkInfo->server = NULL; + CPhidgetRemote_free(phid->networkInfo); + phid->networkInfo = NULL; + CThread_mutex_unlock(&phid->lock); + + if(!activeRemotePhidgets && !activeRemoteManagers && !activeRemoteDictionaries) + { + JoinCentralRemoteThread(); + } + + return result; +} + +int unregisterRemoteManager(CPhidgetManagerHandle phidm) +{ + int result = EPHIDGET_OK; + CServerInfoHandle foundServer; + CServerInfo newServerInfo; + int closeRes; + + //cancel a pending connect + if(phidm->networkInfo->cancelSocket != INVALID_SOCKET) + { + cancelConnect(phidm->networkInfo->cancelSocket); + } +#ifdef USE_ZEROCONF + cancelPendingZeroconfLookups(phidm->networkInfo); +#endif + + CThread_mutex_lock(&activeRemoteManagersLock); + + result = CList_removeFromList((CListHandle *)&activeRemoteManagers, phidm, CPhidgetHandle_areEqual, FALSE, NULL); + + if (result) + { + CThread_mutex_unlock(&activeRemoteManagersLock); + return result; + } + CThread_mutex_unlock(&activeRemoteManagersLock); + + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + if(phidm->networkInfo->server) + { + newServerInfo.server = phidm->networkInfo->server; + + //check to see if there is already a connection to this server + result = CList_findInList((CListHandle)servers, &newServerInfo, CServerInfo_areEqual, (void **)&foundServer); + switch(result) + { + case EPHIDGET_OK: //Found + + if((result = CList_removeFromList((CListHandle *)&foundServer->managers, phidm, CPhidgetManager_areEqual, PFALSE, NULL))) + { + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + + CPhidget_clearStatusFlag(&phidm->status, PHIDGET_SERVER_CONNECTED_FLAG, &phidm->lock); + CPhidget_clearStatusFlag(&phidm->status, PHIDGET_ATTACHED_FLAG, &phidm->lock); + + //stop reports + CThread_mutex_lock(&phidm->networkInfo->server->pdc_lock); + pdc_ignore(foundServer->server->pdcs,phidm->networkInfo->listen_id,NULL,0); + CThread_mutex_unlock(&phidm->networkInfo->server->pdc_lock); + + closeRes = closeServer(foundServer, PFALSE); + + CPhidget_clearStatusFlag(&phidm->status, PHIDGET_REMOTE_FLAG, &phidm->lock); + + /* don't free this here - it may be referenced elsewhere! */ + phidm->networkInfo->server = NULL; + CPhidgetRemote_free(phidm->networkInfo); + phidm->networkInfo = NULL; + + //Run error event for any async commands that didn't complete + if(closeRes != EPHIDGET_CLOSED && foundServer->server->pdcs) + cleanup_pending(foundServer->server->pdcs, phidm); + + break; + case EPHIDGET_NOTFOUND: //Not Found - That's ok, just means it's already closed + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return EPHIDGET_OK; + default: + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + } + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + if(!activeRemotePhidgets && !activeRemoteManagers && !activeRemoteDictionaries) + { + JoinCentralRemoteThread(); + } + + return EPHIDGET_OK; +} + +int unregisterRemoteDictionary(CPhidgetDictionaryHandle dict) +{ + int result = EPHIDGET_OK; + CServerInfoHandle foundServer; + CServerInfo newServerInfo; + CPhidgetDictionaryListenerListHandle trav; + int closeRes = EPHIDGET_OK; + + //cancel a pending connect + if(dict->networkInfo->cancelSocket != INVALID_SOCKET) + { + cancelConnect(dict->networkInfo->cancelSocket); + } +#ifdef USE_ZEROCONF + cancelPendingZeroconfLookups(dict->networkInfo); +#endif + + CThread_mutex_lock(&activeRemoteDictionariesLock); + if ((result = CList_removeFromList((CListHandle *)&activeRemoteDictionaries, dict, CPhidgetHandle_areEqual, FALSE, NULL)) != EPHIDGET_OK) + { + CThread_mutex_unlock(&activeRemoteDictionariesLock); + return result; + } + CThread_mutex_unlock(&activeRemoteDictionariesLock); + + CThread_mutex_lock(&serverLockLock); + CThread_mutex_lock(&serverLock); + CThread_mutex_lock(&dict->lock); + if(dict->networkInfo && dict->networkInfo->server) + { + newServerInfo.server = dict->networkInfo->server; + + //check to see if there is already a connection to this server + result = CList_findInList((CListHandle)servers, &newServerInfo, CServerInfo_areEqual, (void **)&foundServer); + switch(result) + { + case EPHIDGET_OK: //Found + + if((result = CList_removeFromList((CListHandle *)&foundServer->dictionaries, dict, CPhidgetDictionary_areEqual, PFALSE, NULL))) + { + CThread_mutex_unlock(&dict->lock); + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + + //stop reports, remove listeners + CThread_mutex_lock(&dict->listenersLock); + for(trav = dict->listeners; trav; trav = trav->next) + { + CThread_mutex_lock(&dict->networkInfo->server->pdc_lock); + pdc_ignore(foundServer->server->pdcs,trav->listener->listen_id,NULL,0); + CThread_mutex_unlock(&dict->networkInfo->server->pdc_lock); + } + CList_emptyList((CListHandle *)&dict->listeners, PTRUE, CPhidgetDictionaryListener_free); + CThread_mutex_unlock(&dict->listenersLock); + + //closes connection if it's not used anymore + closeRes = closeServer(foundServer, PFALSE); + + break; + case EPHIDGET_NOTFOUND: //Not Found - That's ok, just means it's already closed + break; + default: //ERROR + CThread_mutex_unlock(&dict->lock); + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + return result; + } + //if server needs to be freed it will already have been + CPhidget_clearStatusFlag(&dict->status, PHIDGET_SERVER_CONNECTED_FLAG, NULL); + dict->networkInfo->server = NULL; + CPhidget_clearStatusFlag(&dict->status, PHIDGET_ATTACHED_FLAG, NULL); + + //Run error event for any async commands that didn't complete + if(closeRes != EPHIDGET_CLOSED && foundServer && foundServer->server && foundServer->server->pdcs) + cleanup_pending(foundServer->server->pdcs, dict); + } + + CPhidgetRemote_free(dict->networkInfo); + dict->networkInfo = NULL; + CPhidget_clearStatusFlag(&dict->status, PHIDGET_REMOTE_FLAG, NULL); + + CThread_mutex_unlock(&dict->lock); + CThread_mutex_unlock(&serverLock); + CThread_mutex_unlock(&serverLockLock); + + if(!activeRemotePhidgets && !activeRemoteManagers && !activeRemoteDictionaries) + { + JoinCentralRemoteThread(); + } + + return EPHIDGET_OK; +} + +int unregisterSBCManager(CPhidgetSBCManagerHandle sbcm) +{ + int result = EPHIDGET_OK; + + CThread_mutex_lock(&activeSBCManagersLock); + + result = CList_removeFromList((CListHandle *)&activeSBCManagers, sbcm, CPhidgetHandle_areEqual, FALSE, NULL); + + if (result) + { + CThread_mutex_unlock(&activeSBCManagersLock); + return result; + } + CThread_mutex_unlock(&activeSBCManagersLock); + +#ifdef USE_ZEROCONF + if(!activeRemotePhidgets && !activeRemoteManagers && !activeRemoteDictionaries && !activeSBCManagers) + { + UninitializeZeroconf();//shut down zeroconf + } +#endif + + return EPHIDGET_OK; +} + +int CCONV +CPhidget_openRemoteIPMaster(CPhidgetHandle phid, const char *address, + int port, const char *password) +{ + int result = EPHIDGET_OK; + char portString[6]; + + if((result = CPhidgetRemote_create(&phid->networkInfo))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return result; + } + + if(password) { + if (strlen(password) > 255) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_INVALIDARG; + } + if (!(phid->networkInfo->password = strdup(password))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + + snprintf(portString, sizeof(portString), "%d", port); + if(!(phid->networkInfo->requested_port = strdup(portString))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_NOMEMORY; + } + if(!(phid->networkInfo->requested_address = strdup(address))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_NOMEMORY; + } + + initialize_locks(); + + CPhidget_setStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock); + CPhidget_setStatusFlag(&phid->status, PHIDGET_OPENED_FLAG, &phid->lock); + + result = RegisterRemotePhidget(phid); + + CThread_mutex_unlock(&phid->openCloseLock); + + return result; +} + +int CCONV +CPhidget_openRemoteIP(CPhidgetHandle phid, int serialNumber, const char *address, + int port, const char *password) +{ + TESTPTR(phid) + + if (serialNumber < -1) + return EPHIDGET_INVALIDARG; + + CThread_mutex_lock(&phid->openCloseLock); + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (serialNumber == -1) + phid->specificDevice = PHIDGETOPEN_ANY; + else + phid->specificDevice = PHIDGETOPEN_SERIAL; + phid->serialNumber = serialNumber; + + return CPhidget_openRemoteIPMaster(phid, address, port, password); +} + +int CCONV +CPhidget_openLabelRemoteIP(CPhidgetHandle phid, const char *label, const char *address, + int port, const char *password) +{ + int result; + TESTPTR(phid) + + if(label != NULL && ((result = encodeLabelString(label, NULL, NULL)) != EPHIDGET_OK)) + return result; + + CThread_mutex_lock(&phid->openCloseLock); + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (label == NULL) + phid->specificDevice = PHIDGETOPEN_ANY; + else + { + phid->specificDevice = PHIDGETOPEN_LABEL; + memcpy(phid->label, label, strlen(label)+1); + } + + return CPhidget_openRemoteIPMaster(phid, address, port, password); +} + +int CCONV CPhidget_openRemoteMaster(CPhidgetHandle phid, const char *serverID, const char *password) +{ + int result = EPHIDGET_OK; + + if((result = CPhidgetRemote_create(&phid->networkInfo))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return result; + } + + if(password) { + if (strlen(password) > 255) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_INVALIDARG; + } + if (!(phid->networkInfo->password = strdup(password))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + if(serverID) + { + if (!(phid->networkInfo->requested_serverID = strdup(serverID))) + { + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + + phid->networkInfo->mdns = PTRUE; + + CPhidget_setStatusFlag(&phid->status, PHIDGET_REMOTE_FLAG, &phid->lock); + CPhidget_setStatusFlag(&phid->status, PHIDGET_OPENED_FLAG, &phid->lock); + + result = RegisterRemotePhidget(phid); + + CThread_mutex_unlock(&phid->openCloseLock); + + return result; +} + +int CCONV CPhidget_openRemote(CPhidgetHandle phid, int serialNumber, const char *serverID, const char *password) +{ +#ifdef USE_ZEROCONF + int result = EPHIDGET_OK; + TESTPTR(phid) + + if (serialNumber < -1) + return EPHIDGET_INVALIDARG; + + CThread_mutex_lock(&phid->openCloseLock); + initialize_locks(); + + if((result = InitializeZeroconf())) + { + CThread_mutex_unlock(&phid->openCloseLock); + if(result == EPHIDGET_TRYAGAIN) + return EPHIDGET_TIMEOUT; + return EPHIDGET_UNSUPPORTED; + } + + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (serialNumber == -1) + phid->specificDevice = PHIDGETOPEN_ANY; + else + phid->specificDevice = PHIDGETOPEN_SERIAL; + phid->serialNumber = serialNumber; + + return CPhidget_openRemoteMaster(phid, serverID, password); +#else + return EPHIDGET_UNSUPPORTED; +#endif +} + +int CCONV CPhidget_openLabelRemote(CPhidgetHandle phid, const char *label, const char *serverID, const char *password) +{ +#ifdef USE_ZEROCONF + int result = EPHIDGET_OK; + TESTPTR(phid) + + if(label != NULL && ((result = encodeLabelString(label, NULL, NULL)) != EPHIDGET_OK)) + return result; + + CThread_mutex_lock(&phid->openCloseLock); + initialize_locks(); + + if((result = InitializeZeroconf())) + { + CThread_mutex_unlock(&phid->openCloseLock); + if(result == EPHIDGET_TRYAGAIN) + return EPHIDGET_TIMEOUT; + return EPHIDGET_UNSUPPORTED; + } + + if (CPhidget_statusFlagIsSet(phid->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Phidget handle."); + CThread_mutex_unlock(&phid->openCloseLock); + return EPHIDGET_OK; + } + + if (label == NULL) + phid->specificDevice = PHIDGETOPEN_ANY; + else + { + phid->specificDevice = PHIDGETOPEN_LABEL; + memcpy(phid->label, label, strlen(label)+1); + } + + return CPhidget_openRemoteMaster(phid, serverID, password); +#else + return EPHIDGET_UNSUPPORTED; +#endif +} + +int CCONV CPhidgetManager_openRemoteIP(CPhidgetManagerHandle phidm, const char *address, + int port, const char *password) +{ + int result = EPHIDGET_OK; + char portString[6]; + + TESTPTR(phidm) + + CThread_mutex_lock(&phidm->openCloseLock); + if (CPhidget_statusFlagIsSet(phidm->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Manager handle."); + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_OK; + } + + if((result = CPhidgetRemote_create(&phidm->networkInfo))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return result; + } + + if(password) { + if (strlen(password) > 255) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_INVALIDARG; + } + if (!(phidm->networkInfo->password = strdup(password))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + + snprintf(portString, sizeof(portString), "%d", port); + if(!(phidm->networkInfo->requested_port = strdup(portString))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_NOMEMORY; + } + if(!(phidm->networkInfo->requested_address = strdup(address))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_NOMEMORY; + } + + phidm->state = PHIDGETMANAGER_ACTIVE; + + initialize_locks(); + + CPhidget_setStatusFlag(&phidm->status, PHIDGET_REMOTE_FLAG, &phidm->lock); + CPhidget_setStatusFlag(&phidm->status, PHIDGET_OPENED_FLAG, &phidm->lock); + + result = RegisterRemoteManager(phidm); + + CThread_mutex_unlock(&phidm->openCloseLock); + + return result; +} + +int CCONV CPhidgetManager_openRemote(CPhidgetManagerHandle phidm, const char *serverID, const char *password) +{ +#ifdef USE_ZEROCONF + int result = EPHIDGET_OK; + + CThread_mutex_lock(&phidm->openCloseLock); + + initialize_locks(); + + if((result = InitializeZeroconf())) + { + CThread_mutex_unlock(&phidm->openCloseLock); + if(result == EPHIDGET_TRYAGAIN) + return EPHIDGET_TIMEOUT; + return EPHIDGET_UNSUPPORTED; + } + + if (CPhidget_statusFlagIsSet(phidm->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Manager handle."); + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_OK; + } + + if((result = CPhidgetRemote_create(&phidm->networkInfo))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return result; + } + + if(password) { + if (strlen(password) > 255) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_INVALIDARG; + } + if (!(phidm->networkInfo->password = strdup(password))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + if(serverID) + { + if (!(phidm->networkInfo->requested_serverID = strdup(serverID))) + { + CThread_mutex_unlock(&phidm->openCloseLock); + return EPHIDGET_NOMEMORY; + } + } + + phidm->networkInfo->mdns = PTRUE; + + //we're not connecting to anything, so - always active :) + phidm->state = PHIDGETMANAGER_ACTIVATING; + + CPhidget_setStatusFlag(&phidm->status, PHIDGET_REMOTE_FLAG, &phidm->lock); + CPhidget_setStatusFlag(&phidm->status, PHIDGET_OPENED_FLAG, &phidm->lock); + + result = RegisterRemoteManager(phidm); + if(!result) + { + CPhidgetListHandle trav; + CThread_mutex_lock(&zeroconfPhidgetsLock); + CThread_mutex_lock(&activeRemoteManagersLock); + for (trav=zeroconfPhidgets; trav; trav = trav->next) + { + if (phidm->fptrAttachChange) + phidm->fptrAttachChange((CPhidgetHandle)trav->phid, phidm->fptrAttachChangeptr); + } + phidm->state = PHIDGETMANAGER_ACTIVE; + CThread_mutex_unlock(&activeRemoteManagersLock); + CThread_mutex_unlock(&zeroconfPhidgetsLock); + } + + CThread_mutex_unlock(&phidm->openCloseLock); + return result; +#else + return EPHIDGET_UNSUPPORTED; +#endif +} + +int CCONV CPhidgetDictionary_openRemoteIP(CPhidgetDictionaryHandle dict, const char *address, int port, const char *password) +{ + int result = EPHIDGET_OK; + char portString[6]; + TESTPTRS(dict,address) + + CThread_mutex_lock(&dict->openCloseLock); + if (CPhidget_statusFlagIsSet(dict->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Dictionary handle."); + CThread_mutex_unlock(&dict->openCloseLock); + return EPHIDGET_OK; + } + + if((result = CPhidgetRemote_create(&dict->networkInfo)) != EPHIDGET_OK) + goto fail; + + snprintf(portString, sizeof(portString), "%d", port); + + if((dict->networkInfo->requested_port = strdup(portString)) == NULL + || (dict->networkInfo->requested_address = strdup(address)) == NULL) + { + result = EPHIDGET_NOMEMORY; + goto fail; + } + + if(password) + { + if (strlen(password) > 255) + { + result = EPHIDGET_INVALIDARG; + goto fail; + } + if ((dict->networkInfo->password = strdup(password)) == NULL) + { + result = EPHIDGET_NOMEMORY; + goto fail; + } + } + else + { + dict->networkInfo->password = NULL; + } + + initialize_locks(); + + CPhidget_setStatusFlag(&dict->status, PHIDGET_REMOTE_FLAG, &dict->lock); + CPhidget_setStatusFlag(&dict->status, PHIDGET_OPENED_FLAG, &dict->lock); + + if((result = RegisterRemoteDictionary(dict)) != EPHIDGET_OK) + { + goto fail; + } + + CThread_mutex_unlock(&dict->openCloseLock); + + return EPHIDGET_OK; + +fail: + //This will free any memory allocated in this function. + CPhidget_clearStatusFlag(&dict->status, PHIDGET_REMOTE_FLAG, &dict->lock); + CPhidget_clearStatusFlag(&dict->status, PHIDGET_OPENED_FLAG, &dict->lock); + CPhidgetRemote_free(dict->networkInfo); dict->networkInfo = NULL; + CThread_mutex_unlock(&dict->openCloseLock); + return result; +} + +/* + * dict needs to be valid - should be in opened state + * serverID can be NULL + * password can be NULL + */ +int CCONV CPhidgetDictionary_openRemote(CPhidgetDictionaryHandle dict, const char *serverID, const char *password) +{ +#ifdef USE_ZEROCONF + int result = EPHIDGET_OK; + TESTPTR(dict) + + CThread_mutex_lock(&dict->openCloseLock); + + initialize_locks(); + + if((result = InitializeZeroconf())) + { + if(result == EPHIDGET_TRYAGAIN) + result = EPHIDGET_TIMEOUT; + else + result = EPHIDGET_UNSUPPORTED; + goto fail; + } + + if (CPhidget_statusFlagIsSet(dict->status, PHIDGET_OPENED_FLAG)) + { + LOG(PHIDGET_LOG_WARNING, "Open was called on an already opened Dictionary handle."); + CThread_mutex_unlock(&dict->openCloseLock); + return EPHIDGET_OK; + } + + if((result = CPhidgetRemote_create(&dict->networkInfo)) != EPHIDGET_OK) + goto fail; + + if(password) + { + if (strlen(password) > 255) + { + result = EPHIDGET_INVALIDARG; + goto fail; + } + if ((dict->networkInfo->password = strdup(password)) == NULL) + { + result = EPHIDGET_NOMEMORY; + goto fail; + } + } + if(serverID) + { + if ((dict->networkInfo->requested_serverID = strdup(serverID)) == NULL) + { + result = EPHIDGET_NOMEMORY; + goto fail; + } + } + + dict->networkInfo->mdns = PTRUE; + + CPhidget_setStatusFlag(&dict->status, PHIDGET_REMOTE_FLAG, &dict->lock); + CPhidget_setStatusFlag(&dict->status, PHIDGET_OPENED_FLAG, &dict->lock); + + if((result = RegisterRemoteDictionary(dict)) != EPHIDGET_OK) + { + goto fail; + } + + CThread_mutex_unlock(&dict->openCloseLock); + return EPHIDGET_OK; + +fail: + //This will free any memory allocated in this function. + CPhidget_clearStatusFlag(&dict->status, PHIDGET_REMOTE_FLAG, &dict->lock); + CPhidget_clearStatusFlag(&dict->status, PHIDGET_OPENED_FLAG, &dict->lock); + CPhidgetRemote_free(dict->networkInfo); dict->networkInfo = NULL; + CThread_mutex_unlock(&dict->openCloseLock); + return result; +#else + return EPHIDGET_UNSUPPORTED; +#endif +} + +int CCONV CPhidgetSBCManager_start(CPhidgetSBCManagerHandle sbcm) +{ +#ifdef USE_ZEROCONF + int result = EPHIDGET_OK; + + initialize_locks(); + + if((result = InitializeZeroconf())) + { + if(result == EPHIDGET_TRYAGAIN) + return EPHIDGET_TIMEOUT; + return EPHIDGET_UNSUPPORTED; + } + + sbcm->mdns = PTRUE; + + //we're not connecting to anything, so - always active :) + sbcm->state = PHIDGETMANAGER_ACTIVE; + + result = RegisterSBCManager(sbcm); + if(!result) + { + CPhidgetSBCListHandle trav; + CThread_mutex_lock(&zeroconfSBCsLock); + CThread_mutex_lock(&activeSBCManagersLock); + for (trav=zeroconfSBCs; trav; trav = trav->next) + { + if (sbcm->fptrAttachChange) + sbcm->fptrAttachChange((CPhidgetSBCHandle)trav->sbc, sbcm->fptrAttachChangeptr); + } + CThread_mutex_unlock(&activeSBCManagersLock); + CThread_mutex_unlock(&zeroconfSBCsLock); + } + + return result; +#else + return EPHIDGET_UNSUPPORTED; +#endif +} -- cgit v1.2.3