2 ** libCxClient - Citadel/UX Extensible Client API
3 ** Copyright (c) 2000, Flaming Sword Productions
4 ** Copyright (c) 2001, The Citadel/UX Consortium
7 ** Module: libtransport.o
9 ** Last Revision: 2000-11-30
10 ** Description: Interface to Transport module.
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
35 static int g_CxChatMode = 0,
39 static CXCBHNDL _CbHandles = 0;
40 static char g_CxClientName[32] = "";
41 static int _CxCallback(int cmd, void *data);
42 static void timeout() {}
45 ** CxClRegClient(): (For Developers) Register your client name with
46 ** libCxClient. This gets reported along with the IDEN information passed
47 ** to the server. It should be called before CxClConnect().
49 void CxClRegClient(const char *cl_name) {
51 DPF((DFA,"Developer registered this as \"%s\"", cl_name));
54 ** If this will cause libCxClient to crash, then just die.
56 if(strlen(cl_name)>31) {
57 printf("* * * Fatal Error * * *\n");
58 printf("Invalid use of CxClRegClient(). I expect cl_name to be less than 31 characters in length.\n");
59 printf("cl_name = '%s'\n", cl_name);
60 printf("\nI can't continue. Please re-build your client.\n");
64 strcpy(g_CxClientName, cl_name);
68 ** CxClConnect(): Establish a connection to the server via the Transport layer.
69 ** [Much of this code was gleaned from the "citadel" client]
71 int CxClConnect(const char *host) {
82 char *service = "citadel";
83 char *protocol = "tcp";
85 DPF((DFA,"(Library was built with UNIX_SOCKET support)"));
86 DPF((DFA,"Establishing connection to host \"%s\"",host));
88 memset(&sin, 0, sizeof(sin));
89 sin.sin_family = AF_INET;
91 pse = getservbyname(service, protocol);
93 sin.sin_port = pse->s_port;
94 } else if((sin.sin_port = htons((u_short) atoi(service))) != 0) {
96 sin.sin_port = htons((u_short)504);
98 phe = gethostbyname(host);
100 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
102 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
103 printf("\n* * * Fatal Error * * *\n");
104 printf("System Error: Can't get host entry for '%s'\n", host);
105 printf("Error Details: %s\n\n", strerror(errno));
108 if ((ppe = getprotobyname(protocol)) == 0) {
109 fprintf(stderr, "Can't get %s protocol entry: %s\n",
110 protocol, strerror(errno));
113 if (!strcmp(protocol, "udp")) {
119 s = socket(PF_INET, type, ppe->p_proto);
121 printf("\n* * * Fatal Error * * *\n");
122 printf("System Error: Can't create socket\n");
123 printf("Error Details: %s\n\n", strerror(errno));
126 signal(SIGALRM, timeout);
128 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
129 printf("\n* * * Fatal Error * * *\n");
130 printf("System Error: Can't connect to '%s' [%s]\n",host, service);
131 printf("Error Details: %s\n\n", strerror(errno));
135 signal(SIGALRM, SIG_IGN);
141 if(g_CxClientName[0]) {
142 sprintf(buf,"IDEN 1|1|100|libCxClient/%s (%s)|",VERSION, g_CxClientName);
144 sprintf(buf,"IDEN 1|1|100|libCxClient/%s (unknown)|",VERSION);
152 ** If the server doesn't support Asnychronous mode, then
153 ** we shouldn't try to be asynchronous...
155 if(CHECKRC(rc, RC_OK)) {
162 ** We don't return our socket anymore.
170 ** CxClDisconnect(): Disconnect the socket.
172 void CxClDisconnect() {
174 DPF((DFA,"Caught orders to close socket."));
176 shutdown(g_CxSocket, 0);
180 ** CxClStat(): Send a string to the server.
192 ** CxClSend(): Send a string to the server.
194 void CxClSend(const char *s) {
195 int bytes_written = 0;
199 DPF((DFA,"SEND: \"%s\"", s));
201 ss = (char *)CxMalloc(strlen(s)+2);
202 sprintf(ss,"%s\n",s);
210 while (bytes_written < nbytes) {
211 retval = write(g_CxSocket, &ss[bytes_written],
212 nbytes - bytes_written);
214 write (g_CxSocket, "\n", strlen("\n"));
217 bytes_written = bytes_written + retval;
223 ** ClRecvChr(): Retrieve the next message from the server.
224 ** *********** SOURCE: citadel-source/citmail.c:serv_read()
227 void ClRecvChar(char *buf, int bytes) {
231 while (len < bytes) {
232 rlen = read(g_CxSocket, &buf[len], bytes - len);
241 ** _CxClWait(): Wait on the semaphore.
246 DPF((DFA,"Waiting on Semaphore..."));
247 while(g_CxSemaphore) ;
249 DPF((DFA,"*** LOCKING SESSION ***"));
254 ** _CxClClear(): Clear the semaphore.
259 DPF((DFA,"*** CLEARING SESSION ***"));
264 ** CxClRecv(): Receive a string from the server.
266 int CxClRecv(char *s) {
275 ** At this point, we should wait for the semaphore to be cleared.
276 ** This will prevent multi-threaded clients from pissing all over
277 ** themselves when 2 threads attempt to read at the same time...
282 ** RETRY_RECV when we have a callback and need to re-synch the protocol.
286 DPF((DFA,"for(;message <= bottle;) ;"));
289 ** Read one character at a time.
292 ClRecvChar(&s[i], 1);
293 if (s[i] == '\n' || i == 255)
298 ** If we got a long line, discard characters until the newline.
302 ClRecvChar(&s[i], 1);
306 ** Strip all trailing nonprintables (crlf)
310 DPF((DFA,"I got \"%s\"",s));
315 ** Check to see if the message is prepended with a server result code.
318 (!strcmp(substr,"000")) ||
319 ((substr[0]>='0' && substr[0]<='9') &&
320 (substr[1]>='0' && substr[1]<='9') &&
321 (substr[2]>='0' && substr[2]<='9') &&
322 ((substr[3]==' ') || (substr[3]=='*')))
324 i = (int)strtol(s, (char **)NULL, 10);
325 if(substr[3]=='*') i+=RC_MESGWAIT;
328 ** This removes the result code & other data from the
329 ** returned string. This is _really_ going to mess with
330 ** lots of code. Ugh.
332 DPF((DFA," s: \"%s\"", s));
335 ** Shift the entire string left 4 places.
337 for(tmp = 0; tmp < strlen(s); tmp++) {
338 if(tmp+4 < strlen(s)) s[tmp] = s[tmp+4];
343 DPF((DFA," s: \"%s\"", s));
346 ** Otherwise, we can assume that this is an RC_LISTING entry.
353 ** We wish to clear the semaphore BEFORE executing any callbacks.
354 ** This will help to prevent nasty race conditions. >:)
359 ** This is the only instance of Goto you'll find in
360 ** libCxClient. The point is: Once we're done handling
361 ** an asynchronous message, we need to go back & handle
362 ** other messages as normal.
364 DPF((DFA,"rc = %d (%d)", i, CHECKRC(i, RC_ASYNCMSG)));
368 ** If the server has told us a secret...
370 if(CHECKRC(i, RC_ASYNCMSG)) {
371 DPF((DFA,"Preparing to process async message"));
374 ** Do we have ANY callbacks defined?
379 ** Pass data to callback function, if appropriate.
381 if(_CxCallback(i, s)) {
384 ** ... Callback has failed. We need to
385 ** proactively ignore this message now.
401 DPF((DFA,"Preparing to return rc: %d", i));
407 ** CxClChatInit(): Initialize Chat mode
415 if(g_CxChatMode) return(1);
417 DPF((DFA,"Entering CHAT mode. Please prepare client for chat-mode\n"));
418 DPF((DFA,"communications..."));
426 ** CxClChatShutdown(): Shut down CHAT mode.
428 void CxClChatShutdown() {
435 DPF((DFA,"Exiting CHAT mode."));
440 /*******************************************************************************
441 ** Communications Layer Abstractions: These functions are to be used by the
442 ** higher level functions to handle communications with the servers. The goal
443 ** is to abstract client<->server communication as much as possible, in such a
444 ** way that changes to the underlying transports don't affect CxClient code.
448 ** struct _Cmd_Stack: These are the commands we are sending to the server. It
449 ** is governed by the MAX_SERV_CMD size limit (which, with the current protocol,
452 ** This is an internal structure which is not useful anywhere else in this
457 char cmd[4]; /** Command. **/
458 CXLIST arg_list; /** Arguments. **/
462 ** CxClReq(): Send a request message to the server. On success, returns a numeric
463 ** handle to the request id (which, for now, is the command's offset in the
470 ** CxClReqCancel(): Cancel a pending request. If the request does not exist in
471 ** the CMDSTACK, don't do anything.
472 ** // Unimplemented in Citadel/UX 5.74
474 int CxClSrvCnclReq() {
478 ** CxClCbExists(): Return whether or not a callback exists. If callback
479 ** exists, a pointer is returned to the Callback's handle. Otherwise,
482 CXCBHNDL CxClCbExists(int cmd) {
486 DPF((DFA,"[_CbHandles] @0x%08x", _CbHandles));
489 DPF((DFA,"[x] @0x%08x %i", x, cmd));
491 DPF((DFA,"[*] Found"));
496 DPF((DFA,"[X] Not Found"));
501 ** CxClCbRegister(): Register a Transport Protocol callback.
503 int CxClCbRegister(int cmd, void *func) {
508 DPF((DFA, "Registering callback for '%d' (@0x%08x)", cmd, func));
512 ** Check to see if callback is already in table. If it is, we'll
513 ** assume that the user intended to REPLACE the existing pointer.
515 new = CxClCbExists(cmd);
518 ** If we already exist, we can substitute the existing callback pointer
519 ** with another one and return. No additional effort is required.
522 DPF((DFA, "Replacing existing callback"));
523 new->Function = func;
527 ** Since it doesn't exist in the stack already, we need ta add it
531 DPF((DFA, "Registering new callback"));
532 new = (CXCBHNDL)CxMalloc(sizeof(CXCSCALLBACK));
535 new->Function = func;
538 ** If we haven't defined any callbacks yet, we need to define
539 ** this as the 'head' of the Stack.
546 ** ... Otherwise, we need to add the newest callback to the
547 ** head of the stack.
550 new->next = _CbHandles;
553 DPF((DFA,"[new] @0x%08x",new));
554 DPF((DFA,"->next: @0x%08x",new->next));
555 DPF((DFA,"[_CbHandles] @0x%08x",_CbHandles));
560 ** CxClCbShutdown(): Shutdown the callback subsystem.
562 void CxClCbShutdown() {
566 DPF((DFA,"Shutting down callback subsystem"));
577 ** _CxCallback(): Execute a callback.
580 int _CxCallback(int cmd, void *data) {
585 DPF((DFA, "Executing callback %d", cmd));
586 cb = CxClCbExists(cmd);
588 if(cb) cb->Function(data);