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