Author: jbit
Date: 2006-08-15 02:35:38 +0000 (Tue, 15 Aug 2006)
New Revision: 10081
Added:
trunk/apps/libfcp2/LICENSE
trunk/apps/libfcp2/Makefile
trunk/apps/libfcp2/README
trunk/apps/libfcp2/example.c
trunk/apps/libfcp2/fcp2.c
trunk/apps/libfcp2/fcp2.h
Log:
First commit of a simple C FCPv2 library, plus a simple example program.
Added: trunk/apps/libfcp2/LICENSE
===================================================================
--- trunk/apps/libfcp2/LICENSE 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/LICENSE 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,10 @@
+Copyright (c) 2006, James Lee (jbit at jbit.net)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
+ * Neither the name of the listed copyright holders nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Added: trunk/apps/libfcp2/Makefile
===================================================================
--- trunk/apps/libfcp2/Makefile 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/Makefile 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,22 @@
+# Ultra simple makefile
+
+CFLAGS+=-g -I. -std=c99
+LDFLAGS+=-g
+
+libfcp2_objs=fcp2.o
+example_objs=example.o
+
+all: libfcp2.a example
+
+
+%.o: %.c
+ gcc $(CFLAGS) -c $<
+
+libfcp2.a: fcp2.o
+ ar cr $@ $(libfcp2_objs)
+
+example: $(example_objs)
+ gcc $(LDFLAGS) -L. -o $@ $(example_objs) -lfcp2
+
+clean:
+ rm -f *.o libfcp2.a example
Added: trunk/apps/libfcp2/README
===================================================================
--- trunk/apps/libfcp2/README 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/README 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,10 @@
+This is just a simple C library I wrote to use FCPv2 from a C program.
+It's quite basic at the moment, however I plan to put another layer ontop of
this library so that errors are handled automagicly, and string processing is
done a little more efficiently.
+The API/ABI may well change, so don't depend on it too much.
+
+If you found a bug, have a question, or some sugestions, email me at jbit at
jbit.net
+
+Have fun
+
+James Lee (jbit)
+
Added: trunk/apps/libfcp2/example.c
===================================================================
--- trunk/apps/libfcp2/example.c 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/example.c 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,150 @@
+/*
+libfcp2 - freenet client protocol v2 C library
+example.c: Simple console monitor
+
+Notes:
+ This eats alot of bandwidth since it needs to get all the node info
every referesh.
+ Hopefully in the future FCP will support "GetStatus" or something.
+ Doesn't check for any errors on the FCP connection, so will probably
screw up in some situations
+*/
+
+
+/*
+Copyright (c) 2006, James Lee (jbit at jbit.net)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
+ * Neither the name of the listed copyright holders nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcp2.h>
+
+int main(int argc, char *argv[])
+{
+ FcpMessage *msg;
+ FcpServer *serv;
+ int port=9481;
+ char *host="127.0.0.1";
+
+ if (argc>1)
+ host = argv[1];
+
+ if (argc>2)
+ port = strtol(argv[2], NULL, 0);
+
+
+ /* Create a message buffer to use for decoding (you can have more than
one if you want) */
+ msg = FcpMessageCreate(2*1024);
+ if (msg==NULL)
+ return(1);
+
+ /* Connect to the server */
+ serv = FcpServerConnect(host, port, 64*1024, 64*1024);
+ if (serv==NULL)
+ {
+ printf("Couldn't connect to %s %d!!\n", host, port);
+ return(1);
+ }
+
+ /* Send the client hello message */
+ FcpMessageSend(serv, "ClientHello", 2, "Name", "jbits FCP test",
"ExpectedVersion", "2.0");
+
+ int conn=0, back=0, dis=0, nevr=0;
+ int heart=0;
+ int okay=1;
+ while(okay)
+ {
+ /* Handle the server */
+ if (FcpServerHandle(serv, 1000)<0)
+ {
+ printf("Disconnected!\n");
+ okay=0;
+ if (serv->InputUse==0)
+ break;
+ }
+
+ /* If we've got no data in the input buffer, no point in trying
to decode it */
+ if (serv->InputUse==0)
+ continue;
+
+ do
+ {
+ int ret = FcpMessageRecv(serv, msg);
+ if (ret<0)
+ return(1);
+ if (ret==0)
+ break;
+
+ if (!strcmp(msg->Name,"NodeHello")) /* We got a
connection!!! */
+ {
+ printf("Connected to %s %d (%s)\n", host, port,
FcpMessageField(msg, "Version"));
+ printf(" CONN BACK DIS NEVR\n");
+
+ /* Ask for a list of peers */
+ FcpMessageSend(serv, "ListPeers", 2,
"WithMetadata", "false", "WithVolatile", "true");
+ }
+ else if (!strcmp(msg->Name,"Peer")) /* Peer entry */
+ {
+ const char *status = FcpMessageField(msg,
"volatile.status");
+ // printf("%s: %s\n", FcpMessageField(msg,
"myName"), status);
+
+ /* Figure out the status of the node, and add
it to our accounting */
+ if (status!=NULL)
+ {
+ if (!strcmp(status, "CONNECTED"))
+ conn++;
+ else if (!strcmp(status, "BACKED OFF"))
+ back++;
+ else if (!strcmp(status,
"DISCONNECTED"))
+ dis++;
+ else if (!strcmp(status, "NEVER
CONNECTED"))
+ nevr++;
+ }
+ }
+ else if (!strcmp(msg->Name,"EndListPeers"))
+ {
+ static int chrs = 0;
+
+ /* remove the last line */
+ for (int i=0;i<chrs;i++)
+ putchar('\b');
+
+ /* print node status */
+ chrs = printf("%c %-4d %-4d %-4d %-4d",
heart?'*':' ', conn, back, dis, nevr);
+ fflush(stdout);
+
+ heart = 1-heart;
+
+ /* reset stats */
+ conn = back = dis = nevr = 0;
+
+ /* Wait a second, then ask for another list */
+ sleep(1);
+ FcpMessageSend(serv, "ListPeers", 2,
"WithMetadata", "false", "WithVolatile", "true");
+ }
+ else if
(!strcmp(msg->Name,"CloseConnectionDuplicateClientName"))
+ {
+ printf("\nAnother client stole our
connection!\n");
+ okay = 0;
+ }
+ else
+ FcpMessageDump(msg);
+ } while(1);
+
+ }
+
+ /* Clean up */
+ FcpServerDisconnect(serv);
+ FcpMessageDestroy(msg);
+
+ return(0);
+}
Added: trunk/apps/libfcp2/fcp2.c
===================================================================
--- trunk/apps/libfcp2/fcp2.c 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/fcp2.c 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,579 @@
+/*
+libfcp2 - freenet client protocol v2 C library
+fcp2.c: Low level (ish) FCP communication
+*/
+
+
+/*
+Copyright (c) 2006, James Lee (jbit at jbit.net)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
+ * Neither the name of the listed copyright holders nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* _GNU_SOURCE needed for memmem() function */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <fcp2.h>
+
+# define ALIGN(x, align) ((((uintptr_t)x)+((align)-1))&~((align)-1))
+
+
+
+/*
+FcpServerConnect
+ Connect to a freenet server
+ This blocks, so keep that in mind.
+
+ Inputs
+ hostname: hostname of freenet node to connect to
+ port: port number of FCP
+ ibuf: Size of input buffer
+ obuf: Size of output buffer
+
+ Outputs
+ return: Server structure or NULL
+*/
+FcpServer *FcpServerConnect(const char *hostname, int port, int ibuf, int obuf)
+{
+ FcpServer *serv;
+
+ /* Use local host as default address */
+ if (hostname==NULL || strlen(hostname)==0)
+ hostname = "127.0.0.1";
+
+ /* Use default port */
+ if (port<=0)
+ port = 9481;
+
+ int sockfd;
+ struct hostent *he;
+ struct sockaddr_in addr;
+
+ /* Resolve hostname */
+ if ((he = gethostbyname(hostname)) == NULL)
+ {
+ printf("%s: Failed to resolve (%s)!\n", __FUNCTION__,
strerror(errno));
+ return(NULL);
+ }
+
+ /* Snag socket */
+ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0))<0)
+ {
+ printf("%s: Failed to get socket (%s)!\n", __FUNCTION__,
strerror(errno));
+ return(NULL);
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr = *((struct in_addr *)he->h_addr);
+ memset(&(addr.sin_zero), '\0', 8);
+
+ /* Connect */
+ if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct
sockaddr))<0)
+ {
+ printf("%s: Failed to connect (%s)!\n", __FUNCTION__,
strerror(errno));
+ close(sockfd);
+ return(NULL);
+ }
+
+ /* Allocate accounting and buffers */
+ serv = malloc(sizeof(FcpServer)+ibuf+obuf);
+ if (serv==NULL)
+ {
+ printf("%s: Failed to allocate server buffers (%s)!\n",
__FUNCTION__, strerror(errno));
+ close(sockfd);
+ return(NULL);
+ }
+
+ /* Setup accounting stuff */
+ serv->Name = NULL;
+ serv->Version = NULL;
+ serv->InputSize = ibuf;
+ serv->InputUse = 0;
+ serv->Input = serv->Buffer;
+ serv->OutputSize = obuf;
+ serv->OutputUse = 0;
+ serv->Output = serv->Buffer+ibuf;
+ serv->fd = sockfd;
+
+ return(serv);
+}
+
+
+
+/*
+FcpServerDisconnect
+ Disconnect from server, and destroy the structure
+
+ Inputs
+ serv: Server to disconnect from
+*/
+void FcpServerDisconnect(FcpServer *serv)
+{
+ if (serv->Name!=NULL)
+ free(serv->Name);
+
+ if (serv->Version!=NULL)
+ free(serv->Version);
+
+ close(serv->fd);
+ free(serv);
+}
+
+
+
+/*
+FcpServerFlush
+ Flush output buffer of server
+ NOTE: This won't return until everything is sent!
+
+ Inputs
+ serv: Server to flush
+
+ Outputs
+ return: <0=error, 0=nothing to flush, >0=flushed
+*/
+int FcpServerFlush(FcpServer *serv)
+{
+ int offset = 0;
+
+ /* Keep looping until the buffer is completly empty */
+ while (serv->OutputUse)
+ {
+ ssize_t ret = send(serv->fd, serv->Output+offset,
serv->OutputUse, 0);
+ if (ret<0)
+ return(-1);
+
+ serv->OutputUse-=ret;
+ offset+=ret;
+ }
+
+ return(offset);
+}
+
+
+
+/*
+FcpServerHandle
+ Handle server connection
+ This shouldn't block when timeout is 0.
+*/
+int FcpServerHandle(FcpServer *serv, int timeout)
+{
+ struct pollfd mypol;
+ int retval=0;
+
+ /* Loop until we don't have any more events */
+ do
+ {
+ int pollret;
+ struct pollfd mypol;
+ mypol.fd = serv->fd;
+ mypol.events = POLLERR | POLLHUP;
+
+ /* Only get events that we can use */
+ if (serv->OutputUse)
+ mypol.events |= POLLOUT;
+ if (serv->InputUse<serv->InputSize)
+ mypol.events |= POLLIN;
+
+ /* Look for some events */
+ pollret = poll(&mypol, 1, timeout);
+ if (pollret<0)
+ {
+ printf("%s: poll error (%s)!\n", __FUNCTION__,
strerror(errno));
+ return(-1);
+ }
+
+ if (pollret==0)
+ break;
+
+ timeout = 0;
+
+ /* If we have space, send some data */
+ if (mypol.revents&POLLOUT)
+ {
+ ssize_t ret = send(serv->fd, serv->Output,
serv->OutputUse, 0);
+ if (ret<0)
+ {
+ printf("%s: send error!\n", __FUNCTION__);
+ return(-1);
+ }
+ if (ret>0)
+ {
+ serv->OutputUse-=ret;
+ memmove(serv->Output, serv->Output+ret,
serv->OutputUse);
+ }
+ }
+
+ /* receive some data if we can */
+ if (mypol.revents&POLLIN)
+ {
+ ssize_t ret = recv(serv->fd,
serv->Input+serv->InputUse, serv->InputSize-serv->InputUse, 0);
+ if (ret<0)
+ {
+ printf("%s: recv error!\n", __FUNCTION__);
+ return(-1);
+ }
+ serv->InputUse += ret;
+ retval+=ret;
+ }
+
+ /* if we get an error, die */
+ if (mypol.revents&POLLERR || mypol.revents&POLLHUP)
+ {
+ return(-1);
+ }
+ }
+ while (1);
+
+ return(retval);
+}
+
+
+
+/*
+FcpMessageField
+ Get a message field by name
+
+ Inputs
+ msg: message to get value from
+ name: name of field to get
+
+ Outputs
+ return: Value of attribute or NULL
+*/
+const char *FcpMessageField(FcpMessage *msg, const char *name)
+{
+ /* Just look for the name, and return the value, simple! :) */
+ for (int i=0;i<msg->FieldCount;i++)
+ {
+ if (!strcmp(name, msg->FieldName[i]))
+ return(msg->FieldValue[i]);
+ }
+
+ return(NULL);
+}
+
+
+
+/*
+FcpMessageClear
+ Clear message structure
+
+ Inputs
+ msg: message to clear
+*/
+void FcpMessageClear(FcpMessage *msg)
+{
+ /* We don't need to worry about zeroing the Buffer, since nothing
should be looking there */
+ msg->Name = NULL;
+ msg->FieldCount = 0;
+ msg->FieldName = NULL;
+ msg->FieldValue = NULL;
+}
+
+
+
+/*
+FcpMessageDecode
+ Decode a message into a message structure
+
+ Inputs
+ str: buffer to read from
+ buflen: length of valid data in str
+
+ Outputs
+ msg: message to store decoded info to
+ return: <0 = error, 0 = message fragment, >0 = success
+*/
+int FcpMessageDecode(FcpMessage *msg, const char *str, int buflen)
+{
+ const char *strend = memmem(str, buflen, "\nEndMessage\n", 12);
+ if (strend==NULL)
+ return(0);
+
+ strend+=12;
+
+ /* Calculate length of message */
+ int slen = strend-str;
+
+ /* If the calculated length is bigger than our message buffer, we have
a big problem! */
+ if (slen>msg->BufferSize)
+ {
+ return(-1);
+ }
+
+ /* memcpy is expensive, i'd like to move this later on */
+ memcpy(msg->Buffer, str, slen);
+
+
+ /* Find out where our pointer list should be, remembering processors
arn't magic and need alignment */
+ char **ptrs = (void *)ALIGN(msg->Buffer+slen, sizeof(const char*));
+
+ /* calculate maximum number of fields we can support */
+ const int maxfield =
(msg->BufferSize-((uintptr_t)ptrs-(uintptr_t)msg->Buffer))/(sizeof(const char
*)*2);
+
+ /*
+ replace all end of lines with nul terminator
+ XXX: I'm not sure if FCP allows "\r\n" line termination. If it
does, some of this library will have issues.
+ */
+ for (int i=0;i<slen;i++)
+ {
+ if (msg->Buffer[i]=='\n')
+ msg->Buffer[i]='\0';
+ }
+
+ msg->Name = msg->Buffer;
+
+ const char *end = msg->Buffer + slen;
+ char *ptr = msg->Buffer + strlen(msg->Name);
+ size_t fields;
+ int ended = 0;
+
+ /* find all fields */
+ for (fields=0;fields<maxfield&&ptr<end;fields++)
+ {
+ while (*ptr=='\0'&&ptr<end)
+ ptr++;
+
+ if (ptr>=end)
+ break;
+
+ if (!strncmp(ptr, "EndMessage", end-ptr))
+ {
+ ptr += strlen(ptr);
+ ended = 1;
+ break;
+ }
+
+ ptrs[fields] = ptr;
+
+ ptr += strlen(ptr);
+ }
+
+ /* too many fields in message!!! */
+ if (fields>=maxfield)
+ {
+ printf("%s: Message '%s' has too many fields (>%d)!\n",
__FUNCTION__, msg->Name, fields);
+ FcpMessageClear(msg);
+ return(-ENOMEM);
+ }
+
+ /* No ending, but it was detected above, something is up.. */
+ if (!ended)
+ {
+ printf("%s: Message '%s' is malformed!\n", __FUNCTION__,
msg->Name);
+ FcpMessageClear(msg);
+ return(-1);
+ }
+
+ /* Take into account the line terminator, for size reasons */
+ if (ptr<end)
+ {
+ int pos = ptr-msg->Buffer;
+ if (str[pos]=='\n')
+ ptr++;
+ }
+
+ msg->FieldName = (const char**)ptrs;
+ msg->FieldValue = (const char**)ptrs+fields;
+
+ /* replace all fields ='s with nuls, and setup value pointers */
+ for (size_t i=0;i<fields;i++)
+ {
+ char *v = strchr(ptrs[i], '=');
+
+ if (v==NULL)
+ {
+ /* If we get here, we have a line without an =, not
sure what to do with it */
+ msg->FieldName[i] = NULL;
+ msg->FieldValue[i] = ptrs[i];
+ }
+ else
+ {
+ *v = '\0';
+ msg->FieldValue[i] = v+1;
+ }
+ }
+
+ msg->FieldCount = fields;
+
+ return(ptr-msg->Buffer);
+}
+
+
+
+/*
+FcpMessageCreate
+ Create and setup a new message structure
+
+ Inputs
+ size: Size of messages data buffer (<0 = sensible default)
+
+ Outputs
+ return: A new message or NULL
+*/
+FcpMessage *FcpMessageCreate(int size)
+{
+ FcpMessage *msg;
+
+ if (size<0)
+ size = 8192;
+
+ msg = malloc(sizeof(FcpMessage)+size);
+ if (msg==NULL)
+ return(NULL);
+
+ msg->BufferSize = size;
+ FcpMessageClear(msg);
+
+ return(msg);
+}
+
+
+
+/*
+FcpMessageDestroy
+ Destroy a message
+
+ Inputs
+ msg: Message to destroy
+*/
+void FcpMessageDestroy(FcpMessage *msg)
+{
+ free(msg);
+}
+
+
+
+/*
+FcpMessageDump
+ Dump message details to stdout
+
+ Inputs
+ msg: Message to dump
+*/
+void FcpMessageDump(FcpMessage *msg)
+{
+ printf("<%s>\n", msg->Name);
+ for (size_t i=0;i<msg->FieldCount;i++)
+ printf(" %3d: [%s]=%s\n", i, msg->FieldName[i],
msg->FieldValue[i]);
+}
+
+
+
+/*
+FcpMessageSend
+ Send a message, usualy just puts the data on the output queue
+
+ Inputs
+ serv: Server to send to
+ name: The message name
+ count: Number of fields
+ ...: Alternating (const char*) of field name and field value
+
+ Outputs
+ return: <0=error, 0=not sent, >0=sent
+*/
+int FcpMessageSend(FcpServer *serv, const char *name, int count, ...)
+{
+ va_list ap;
+ char *end = serv->Output+serv->OutputSize;
+ char *buf = serv->Output+serv->OutputUse;
+
+ /* Check we have enough space on the output queue for the message name
and terminator */
+ if (strlen(name)+12>(end-buf))
+ return(-1);
+
+ /* Copy name and line terminator into output buffer */
+ memcpy(buf, name, strlen(name));
+ buf+=strlen(name);
+ *(buf++) = '\n';
+
+
+ va_start(ap, count);
+
+
+ for (int i=0;i<count;i++)
+ {
+ const char *n = va_arg(ap, const char *);
+ const char *v = va_arg(ap, const char *);
+
+ /* check to make sure we have enough space to add this field
and the terminator */
+ if (strlen(n)+strlen(v)+13>(end-buf))
+ {
+ va_end(ap);
+ return(-1);
+ }
+
+ /* Copy <n>=<v>\n to buffer */
+ memcpy(buf, n, strlen(n));
+ buf+=strlen(n);
+ *(buf++) = '=';
+ memcpy(buf, v, strlen(v));
+ buf+=strlen(v);
+ *(buf++) = '\n';
+ }
+ va_end(ap);
+
+ /* We shouldn't actually need this check, but do it anyway */
+ if (11>(end-buf))
+ return(-1);
+
+ memcpy(buf, "EndMessage\n", 11);
+ buf+=11;
+
+ serv->OutputUse = buf - serv->Output;
+
+ return(1);
+}
+
+
+
+/*
+FcpMessageRecv
+ Decode messsage from server input buffer
+
+ Inputs
+ serv: Server to decode message from
+ msg: Message to store data in
+
+ Outputs
+ return: <0=error, 0=not sent, >0=sent
+*/
+int FcpMessageRecv(FcpServer *serv, FcpMessage *msg)
+{
+ int size;
+ /* Try to decode what's in the buffer */
+ size = FcpMessageDecode(msg, serv->Input, serv->InputUse);
+
+ /* If we can't decode, just return */
+ if (size<=0)
+ return(size);
+
+ /* If we've decoded we can flush the data from the input buffer... */
+ memmove(serv->Input, serv->Input+size, serv->InputSize-size);
+ serv->InputUse-=size;
+ return(1);
+}
+
Added: trunk/apps/libfcp2/fcp2.h
===================================================================
--- trunk/apps/libfcp2/fcp2.h 2006-08-15 00:59:35 UTC (rev 10080)
+++ trunk/apps/libfcp2/fcp2.h 2006-08-15 02:35:38 UTC (rev 10081)
@@ -0,0 +1,62 @@
+#ifndef __FCP2_H__
+#define __FCP2_H__ 1
+
+/* stddef.h for size_t */
+
+#include <stddef.h>
+/*
+FcpMessage
+ All pointers here are within the Buffer (including the Field pointer
array)
+ All strings are nul terminated.
+ Buffer layout:
+ original message (with line feeds and field ='s replaced with
0's)
+ -- pad to pointer alignment --
+ FieldName pointer array
+ FieldValue pointer array
+*/
+typedef struct
+{
+ const char *Name; /* Name of message */
+ size_t FieldCount; /* How many fields */
+ const char **FieldName; /* Names of fields */
+ const char **FieldValue; /* Names of fields */
+ size_t BufferSize; /* Size (in bytes) of below Buffer */
+ char Buffer[];
+} FcpMessage;
+
+/*
+FcpServer
+ Name and version are strdup()'ed
+ Input and Output point to memory inside Buffer
+*/
+
+typedef struct
+{
+ char *Name; /* Name of server (FBI node) */
+ char *Version; /* Version of server (Fred,0.7,1.0,941)
*/
+ size_t InputSize, InputUse; /* Size of input buffer, and buffer
usage (in bytes) */
+ char *Input; /* Pointer to input buffer */
+ size_t OutputSize, OutputUse; /* Size of output buffer, and buffer
usage (in bytes) */
+ char *Output; /* Pointer to output buffer */
+
+ int fd; /* Socket of server */
+ char Buffer[];
+} FcpServer;
+
+/* Low levelish functions */
+FcpServer *FcpServerConnect (const char *hostname, int port, int ibuf, int
obuf);
+void FcpServerDisconnect(FcpServer *s);
+int FcpServerHandle (FcpServer *serv, int timeout);
+
+FcpMessage *FcpMessageCreate(int size);
+const char *FcpMessageField(FcpMessage *msg, const char *name);
+void FcpMessageClear (FcpMessage *msg);
+int FcpMessageDecode (FcpMessage *msg, const char *str, int buflen);
+void FcpMessageDump (FcpMessage *msg);
+void FcpMessageDestroy(FcpMessage *msg);
+int FcpMessageSend (FcpServer *serv, const char *name, int count, ...);
+int FcpMessageRecv (FcpServer *serv, FcpMessage *msg);
+
+
+#endif /* __FCP2_H__ */
+