version 0.1.9
[fms.git] / src / freenet / fcpv2.cpp
1 #include "../../include/freenet/fcpv2.h"\r
2 #include <cstdio>\r
3 #include <cstdarg>\r
4 #include <sstream>\r
5 \r
6 #ifdef _WIN32\r
7         #include <ws2tcpip.h>\r
8 #else\r
9         #include <netdb.h>\r
10         #include <netinet/in.h>\r
11 #endif\r
12 \r
13 /* XMEM doesn't play nice with strtok - should replace strtok with something else anyway\r
14 #ifdef XMEM\r
15         #include <xmem.h>\r
16 #endif\r
17 */\r
18 \r
19 #ifdef _WIN32\r
20         bool FCPv2::m_wsastartup=false;\r
21 #endif\r
22 \r
23 \r
24 \r
25 FCPv2::FCPv2()\r
26 {\r
27 #ifdef _WIN32\r
28         if(m_wsastartup==false)\r
29         {\r
30                 WSAData wsadata;\r
31                 WSAStartup(MAKEWORD(2,2),&wsadata);\r
32                 m_wsastartup=true;\r
33         }\r
34 #endif\r
35 \r
36         // initialize socket to server\r
37         m_serversocket=-1;\r
38 \r
39         // initialize buffers\r
40         m_tempbuffer=new char[65535];\r
41 \r
42 }\r
43 \r
44 \r
45 FCPv2::~FCPv2()\r
46 {\r
47         Disconnect();\r
48 #ifdef _WIN32\r
49         WSACleanup();\r
50 #endif\r
51 \r
52         delete [] m_tempbuffer;\r
53 \r
54 }\r
55 \r
56 \r
57 const bool FCPv2::Connect(const char *host, const int port)\r
58 {\r
59         // disconnect socket to server if it is currently open\r
60         if(Connected())\r
61         {\r
62                 Disconnect();\r
63         }\r
64 \r
65         int rval=-1;\r
66         struct sockaddr_storage m_serveraddr;\r
67 \r
68         std::ostringstream portstring;\r
69         addrinfo hint,*result,*current;\r
70         result=NULL;\r
71         portstring << port;\r
72 \r
73         memset(&hint,0,sizeof(addrinfo));\r
74         hint.ai_socktype=SOCK_STREAM;\r
75         rval=getaddrinfo(host,portstring.str().c_str(),&hint,&result);\r
76 \r
77         // erase any data in buffers\r
78         m_sendbuffer.clear();\r
79         m_receivebuffer.clear();\r
80 \r
81         if(result)\r
82         {\r
83                 for(current=result; current!=NULL && m_serversocket==-1; current=current->ai_next)\r
84                 {\r
85                         memset(&m_serveraddr,0,sizeof(struct sockaddr_storage));\r
86 \r
87                         m_serversocket=socket(current->ai_family,current->ai_socktype,current->ai_protocol);\r
88 \r
89                         if(m_serversocket!=-1)\r
90                         {\r
91                                 rval=connect(m_serversocket,current->ai_addr,current->ai_addrlen);\r
92                                 if(rval==-1)\r
93                                 {\r
94                                         Disconnect();\r
95                                 }\r
96                         }\r
97                 }\r
98 \r
99                 freeaddrinfo(result);\r
100         }\r
101 \r
102         if(rval==0)\r
103         {\r
104                 return true;\r
105         }\r
106         else\r
107         {\r
108                 return false;\r
109         }\r
110 \r
111 }\r
112 \r
113 const bool FCPv2::Disconnect()\r
114 {\r
115         if(Connected())\r
116         {\r
117         #ifdef _WIN32\r
118                 closesocket(m_serversocket);\r
119         #else\r
120                 close(m_serversocket);\r
121         #endif\r
122                 m_serversocket=-1;\r
123         }\r
124         return true;\r
125 }\r
126 \r
127 int FCPv2::FindOnReceiveBuffer(const char *text)\r
128 {\r
129         bool found;\r
130         std::vector<char>::size_type i,j;\r
131         size_t tlen=strlen(text);\r
132 \r
133         if(m_receivebuffer.size()>=tlen)\r
134         {\r
135                 for(i=0; i<=m_receivebuffer.size()-tlen; i++)\r
136                 {\r
137                         found=true;\r
138                         for(j=0; j<tlen; j++)\r
139                         {\r
140                                 if(m_receivebuffer[i+j]!=text[j])\r
141                                 {\r
142                                         found=false;\r
143                                         j=tlen;\r
144                                 }\r
145                         }\r
146                         if(found==true)\r
147                         {\r
148                                 return i;\r
149                         }\r
150                 }\r
151         }\r
152 \r
153         return -1;\r
154 }\r
155 \r
156 FCPMessage FCPv2::ReceiveMessage()\r
157 {\r
158         int field=0;\r
159         int len=0;\r
160         int endlen=0;\r
161         int endmessage=-1;\r
162         char *buffpos;\r
163         char *prevpos;\r
164         char *buffer;\r
165 \r
166         FCPMessage message;\r
167 \r
168         // there is data on the receive buffer\r
169         if(m_receivebuffer.size()>0)\r
170         {\r
171 \r
172                 // find Data on a line by itself following AllData\r
173                 if(FindOnReceiveBuffer("AllData\n")==0)\r
174                 {\r
175                         endmessage=FindOnReceiveBuffer("\nData\n");\r
176                         if(endmessage!=-1)\r
177                         {\r
178                                 endmessage++;\r
179                                 endlen=5;\r
180                         }\r
181                 }\r
182                 // otherwise this is a regular message - search for EndMessage\r
183                 else\r
184                 {\r
185                         endmessage=FindOnReceiveBuffer("EndMessage\n");\r
186                         endlen=11;\r
187                 }\r
188 \r
189                 // continue if we found "EndMessage\n" or "Data\n"\r
190                 if(endmessage!=-1)\r
191                 {\r
192                         // total length of message (including ending \n)\r
193                         len=endmessage+endlen;\r
194 \r
195                         // allocate space for message\r
196                         buffer=new char[len+1];\r
197 \r
198                         // copy message from receive buffer to message buffer\r
199                         std::copy(m_receivebuffer.begin(),m_receivebuffer.begin()+len,buffer);\r
200                         buffer[len]='\0';\r
201 \r
202                         // remove from receive buffer\r
203                         m_receivebuffer.erase(m_receivebuffer.begin(),m_receivebuffer.begin()+len);\r
204 \r
205                         // set buffer position\r
206                         buffpos=buffer;\r
207 \r
208                         // find message name\r
209                         buffpos=strtok(buffer,"\n");\r
210                         message.SetName(buffer);\r
211 \r
212                         do\r
213                         {\r
214                                 // find next field\r
215                                 prevpos=buffpos;\r
216                                 buffpos=strtok(NULL,"=");\r
217 \r
218                                 // continue if we aren't at the end of a regular message, or at Data for an AllData message\r
219                                 if(strncmp(buffpos,"EndMessage\n",11)!=0 && strncmp(buffpos,"Data\n",5)!=0)     //!(strncmp(message->MessageName,"AllData",7)==0 && strncmp(buffpos,"Data\n",5)==0))\r
220                                 {\r
221 \r
222                                         // find next value\r
223                                         prevpos=buffpos;\r
224                                         buffpos=strtok(NULL,"\n");\r
225 \r
226                                         if(prevpos && buffpos)\r
227                                         {\r
228                                                 message[prevpos]=buffpos;\r
229                                         }\r
230 \r
231                                         field++;\r
232                                 }\r
233                                 else\r
234                                 {\r
235                                         buffpos=0;\r
236                                 }\r
237 \r
238                         }while(buffpos!=0);\r
239 \r
240                         delete [] buffer;\r
241 \r
242                 }\r
243         }\r
244 \r
245         return message;\r
246 }\r
247 \r
248 const long FCPv2::ReceiveRaw(char *data, long &datalen)\r
249 {\r
250         long len=0;\r
251         if(m_receivebuffer.size()>0 && datalen>0)\r
252         {\r
253                 if(datalen>m_receivebuffer.size())\r
254                 {\r
255                         len=m_receivebuffer.size();\r
256                 }\r
257                 else\r
258                 {\r
259                         len=datalen;\r
260                 }\r
261 \r
262                 std::copy(m_receivebuffer.begin(),m_receivebuffer.begin()+len,data);\r
263 \r
264                 // erase bytes from receive buffer\r
265                 m_receivebuffer.erase(m_receivebuffer.begin(),m_receivebuffer.begin()+len);\r
266 \r
267         }\r
268         datalen=len;\r
269         return datalen;\r
270 }\r
271 \r
272 void FCPv2::SendBufferedText(const char *text)\r
273 {\r
274         unsigned int i;\r
275         for(i=0; i<strlen(text); i++)\r
276         {\r
277                 m_sendbuffer.push_back(text[i]);\r
278         }\r
279 }\r
280 \r
281 void FCPv2::SendBufferedRaw(const char *data, const long len)\r
282 {\r
283         int i;\r
284         for(i=0; i<len; i++)\r
285         {\r
286                 m_sendbuffer.push_back(data[i]);\r
287         }\r
288 }\r
289 \r
290 const int FCPv2::SendMessage(const char *messagename, const int fieldcount, ...)\r
291 {\r
292         va_list args;\r
293         const char *field;\r
294         const char *val;\r
295         std::vector<char>::size_type bytecount=0;\r
296         int i;\r
297         std::vector<char>::size_type startlen;\r
298 \r
299         startlen=m_sendbuffer.size();\r
300 \r
301         SendBufferedText(messagename);\r
302         SendBufferedText("\n");\r
303 \r
304         va_start(args,fieldcount);\r
305 \r
306         for(i=0; i<fieldcount; i++)\r
307         {\r
308                 field=va_arg(args,const char *);\r
309                 val=va_arg(args,const char *);\r
310 \r
311                 SendBufferedText(field);\r
312                 SendBufferedText("=");\r
313                 SendBufferedText(val);\r
314                 SendBufferedText("\n");\r
315         }\r
316 \r
317         SendBufferedText("EndMessage\n");\r
318 \r
319         bytecount=m_sendbuffer.size()-startlen;\r
320         \r
321         va_end(args);\r
322 \r
323         return bytecount;\r
324 }\r
325 \r
326 const int FCPv2::SendMessage(FCPMessage &message)\r
327 {\r
328         std::vector<char>::size_type bytecount=0;\r
329         std::vector<char>::size_type startlen;\r
330         FCPMessage::iterator i;\r
331 \r
332         startlen=m_sendbuffer.size();\r
333 \r
334         if(message.GetName()!="")\r
335         {\r
336                 SendBufferedText(message.GetName().c_str());\r
337                 SendBufferedText("\n");\r
338 \r
339                 for(i=message.begin(); i!=message.end(); i++)\r
340                 {\r
341                         SendBufferedText((*i).first.c_str());\r
342                         SendBufferedText("=");\r
343                         SendBufferedText((*i).second.c_str());\r
344                         SendBufferedText("\n");\r
345                 }\r
346 \r
347                 SendBufferedText("EndMessage\n");\r
348         }\r
349 \r
350         bytecount=m_sendbuffer.size()-startlen;\r
351 \r
352         return bytecount;\r
353 }\r
354 \r
355 \r
356 const int FCPv2::SendRaw(const char *data, const int datalen)\r
357 {\r
358         int bytecount=datalen;\r
359 \r
360         if(bytecount>0)\r
361         {\r
362                 SendBufferedRaw(data,datalen);\r
363         }\r
364 \r
365         return bytecount;\r
366 \r
367 }\r
368 \r
369 void FCPv2::SocketReceive()\r
370 {\r
371         int len=0;\r
372 \r
373         len=recv(m_serversocket,m_tempbuffer,65535,0);\r
374 \r
375         if(len>0)\r
376         {\r
377 \r
378                 m_receivebuffer.resize(m_receivebuffer.size()+len);\r
379                 std::copy(m_tempbuffer,&m_tempbuffer[len],m_receivebuffer.end()-len);\r
380 \r
381         }\r
382         // there was an error or server closed connection  - disconnect socket\r
383         else\r
384         {\r
385                 Disconnect();\r
386         }\r
387 }\r
388 \r
389 void FCPv2::SocketSend()\r
390 {\r
391         int len=0;\r
392         if(m_sendbuffer.size()>0)\r
393         {\r
394                 len=send(m_serversocket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
395                 if(len>0)\r
396                 {\r
397                         // move remaining data in buffer to beginning of buffer (erase the bytes we just sent)\r
398                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+len);\r
399                 }\r
400                 // there was an error with send - disconnect socket\r
401                 else\r
402                 {\r
403                         Disconnect();\r
404                 }\r
405         }\r
406 }\r
407 \r
408 \r
409 const bool FCPv2::Update(const long waittime)\r
410 {\r
411 \r
412         if(Connected())\r
413         {\r
414                 m_timeval.tv_sec=waittime;\r
415                 m_timeval.tv_usec=0;\r
416 \r
417                 FD_ZERO(&m_readfs);\r
418                 FD_ZERO(&m_writefs);\r
419 \r
420                 FD_SET(m_serversocket,&m_readfs);\r
421                 \r
422                 if(m_sendbuffer.size()>0)\r
423                 {\r
424                         FD_SET(m_serversocket,&m_writefs);\r
425                 }\r
426 \r
427                 select(m_serversocket+1,&m_readfs,&m_writefs,0,&m_timeval);\r
428 \r
429                 if(FD_ISSET(m_serversocket,&m_readfs))\r
430                 {\r
431                         SocketReceive();\r
432                 }\r
433                 if(Connected() && FD_ISSET(m_serversocket,&m_writefs))\r
434                 {\r
435                         SocketSend();\r
436                 }\r
437 \r
438                 return true;\r
439 \r
440         }\r
441         else\r
442         {\r
443                 return false;\r
444         }\r
445 \r
446 }\r