version 0.1.7
[fms.git] / src / nntp / nntpconnection.cpp
1 #include "../../include/nntp/nntpconnection.h"\r
2 #include "../../include/nntp/uwildmat.h"\r
3 #include "../../include/stringfunctions.h"\r
4 #include "../../include/datetime.h"\r
5 #include "../../include/boardlist.h"\r
6 #include "../../include/message.h"\r
7 #include "../../include/messagelist.h"\r
8 #include "../../include/option.h"\r
9 \r
10 #include <algorithm>\r
11 \r
12 //#include <zthread/Thread.h>\r
13 #include "../../include/pthreadwrapper/thread.h"\r
14 \r
15 #ifdef XMEM\r
16         #include <xmem.h>\r
17 #endif\r
18 \r
19 NNTPConnection::NNTPConnection(SOCKET sock)\r
20 {\r
21         std::string tempval;\r
22 \r
23         m_socket=sock;\r
24         m_tempbuffer.resize(32768);\r
25         \r
26         m_status.m_isposting=false;\r
27         m_status.m_allowpost=false;\r
28         m_status.m_boardid=-1;\r
29         m_status.m_messageid=-1;\r
30         m_status.m_mode=MODE_NONE;\r
31 \r
32         Option::Instance()->Get("NNTPAllowPost",tempval);\r
33         if(tempval=="true")\r
34         {\r
35                 m_status.m_allowpost=true;\r
36         }\r
37 \r
38 }\r
39 \r
40 NNTPConnection::~NNTPConnection()\r
41 {\r
42 \r
43 }\r
44 \r
45 void NNTPConnection::Disconnect()\r
46 {\r
47         if(m_socket!=INVALID_SOCKET)\r
48         {\r
49         #ifdef _WIN32\r
50                 closesocket(m_socket);\r
51         #else\r
52                 close(m_socket);\r
53         #endif\r
54                 m_socket=INVALID_SOCKET;\r
55         }\r
56 }\r
57 \r
58 std::vector<char>::iterator NNTPConnection::Find(std::vector<char> &buffer, const std::string &val)\r
59 {\r
60         return std::search(buffer.begin(),buffer.end(),val.begin(),val.end());\r
61 }\r
62 \r
63 const bool NNTPConnection::HandleArticleCommand(const NNTPCommand &command)\r
64 {\r
65 \r
66         SendArticleParts(command);\r
67         \r
68         return true;\r
69 }\r
70 \r
71 const bool NNTPConnection::HandleBodyCommand(const NNTPCommand &command)\r
72 {\r
73         SendArticleParts(command);\r
74 \r
75         return true;\r
76 }\r
77 \r
78 const bool NNTPConnection::HandleCapabilitiesCommand(const NNTPCommand &command)\r
79 {\r
80         \r
81         SendBufferedLine("101 Capability list :");\r
82         SendBufferedLine("VERSION 2");\r
83         SendBufferedLine("MODE-READER");\r
84         SendBufferedLine("READER");\r
85         SendBufferedLine("LIST OVERVIEW.FMT");\r
86         SendBufferedLine("OVER MSGID");\r
87         if(m_status.m_allowpost==true)\r
88         {\r
89                 SendBufferedLine("POST");\r
90         }\r
91         SendBufferedLine(".");\r
92         \r
93         return true;\r
94 }\r
95 \r
96 const bool NNTPConnection::HandleCommand(const NNTPCommand &command)\r
97 {\r
98         if(command.m_command=="QUIT")\r
99         {\r
100                 return HandleQuitCommand(command);\r
101         }\r
102         if(command.m_command=="MODE")\r
103         {\r
104                 return HandleModeCommand(command);\r
105         }\r
106         if(command.m_command=="CAPABILITIES")\r
107         {\r
108                 return HandleCapabilitiesCommand(command);\r
109         }\r
110         if(command.m_command=="HELP")\r
111         {\r
112                 return HandleHelpCommand(command);\r
113         }\r
114         if(command.m_command=="DATE")\r
115         {\r
116                 return HandleDateCommand(command);\r
117         }\r
118         if(command.m_command=="LIST")\r
119         {\r
120                 return HandleListCommand(command);\r
121         }\r
122         if(command.m_command=="GROUP")\r
123         {\r
124                 return HandleGroupCommand(command);\r
125         }\r
126         if(command.m_command=="LISTGROUP")\r
127         {\r
128                 return HandleListGroupCommand(command);\r
129         }\r
130         if(command.m_command=="LAST")\r
131         {\r
132                 return HandleLastCommand(command);\r
133         }\r
134         if(command.m_command=="NEXT")\r
135         {\r
136                 return HandleNextCommand(command);\r
137         }\r
138         if(command.m_command=="ARTICLE")\r
139         {\r
140                 return HandleArticleCommand(command);\r
141         }\r
142         if(command.m_command=="HEAD")\r
143         {\r
144                 return HandleHeadCommand(command);\r
145         }\r
146         if(command.m_command=="BODY")\r
147         {\r
148                 return HandleBodyCommand(command);\r
149         }\r
150         if(command.m_command=="STAT")\r
151         {\r
152                 return HandleStatCommand(command);\r
153         }\r
154         if(command.m_command=="NEWGROUPS")\r
155         {\r
156                 return HandleNewGroupsCommand(command);\r
157         }\r
158         if(command.m_command=="POST")\r
159         {\r
160                 return HandlePostCommand(command);\r
161         }\r
162         if(command.m_command=="OVER" || command.m_command=="XOVER")\r
163         {\r
164                 return HandleOverCommand(command);\r
165         }\r
166 \r
167         return false;\r
168 }\r
169 \r
170 const bool NNTPConnection::HandleDateCommand(const NNTPCommand &command)\r
171 {\r
172         DateTime now;\r
173         now.SetToGMTime();\r
174         SendBufferedLine("111 "+now.Format("%Y%m%d%H%M%S"));\r
175         return true;\r
176 }\r
177 \r
178 const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)\r
179 {\r
180         if(command.m_arguments.size()==1)\r
181         {\r
182                 Board board;\r
183                 if(board.Load(command.m_arguments[0])==true)\r
184                 {\r
185                         std::ostringstream tempstr;\r
186 \r
187                         tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
188 \r
189                         SendBufferedLine(tempstr.str());\r
190 \r
191                         // set the current boardid to this one\r
192                         m_status.m_boardid=board.GetBoardID();\r
193                         //set the first message id, -1 if there are no messages\r
194                         board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
195 \r
196                 }\r
197                 else\r
198                 {\r
199                         SendBufferedLine("411 No such newsgroup");\r
200                 }\r
201         }\r
202         else\r
203         {\r
204                 SendBufferedLine("501 Syntax error");\r
205                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleGroupCommand syntax error");\r
206         }\r
207 \r
208         return true;\r
209 }\r
210 \r
211 const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)\r
212 {\r
213         \r
214         SendArticleParts(command);\r
215 \r
216         return true;\r
217 }\r
218 \r
219 const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)\r
220 {\r
221         SendBufferedLine("100 Help text follows");\r
222         SendBufferedLine("There is no help text");\r
223         SendBufferedLine(".");\r
224 \r
225         return true;\r
226 }\r
227 \r
228 const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)\r
229 {\r
230         if(m_status.m_boardid!=-1)\r
231         {\r
232                 if(m_status.m_messageid!=-1)\r
233                 {\r
234                         Message mess;\r
235 \r
236                         if(mess.LoadPrevious(m_status.m_messageid,m_status.m_boardid))\r
237                         {\r
238                                 std::ostringstream tempstr;\r
239 \r
240                                 m_status.m_messageid=mess.GetMessageID();\r
241 \r
242                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
243 \r
244                                 SendBufferedLine(tempstr.str());\r
245 \r
246                         }\r
247                         else\r
248                         {\r
249                                 SendBufferedLine("422 No previous article in this group");\r
250                         }\r
251                 }\r
252                 else\r
253                 {\r
254                         SendBufferedLine("420 Current article number is invalid");\r
255                 }\r
256         }\r
257         else\r
258         {\r
259                 SendBufferedLine("412 No newsgroup selected");\r
260         }\r
261 \r
262         return true;\r
263 }\r
264 \r
265 const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)\r
266 {\r
267 \r
268         int type=1;     // default LIST type is active\r
269         std::string arg1="";\r
270         std::string arg2="";\r
271 \r
272         // type of LIST\r
273         if(command.m_arguments.size()>0)\r
274         {\r
275                 StringFunctions::UpperCase(command.m_arguments[0],arg1);\r
276                 if(arg1=="ACTIVE")\r
277                 {\r
278                         type=1;\r
279                 }\r
280                 else if(arg1=="NEWSGROUPS")\r
281                 {\r
282                         type=2;\r
283                 }\r
284                 else if(arg1=="OVERVIEW.FMT")\r
285                 {\r
286                         type=3;\r
287                 }\r
288                 else\r
289                 {\r
290                         type=0;\r
291                 }\r
292         }\r
293         // wildmat\r
294         if(command.m_arguments.size()>1)\r
295         {\r
296                 arg2=command.m_arguments[1];\r
297         }\r
298 \r
299         // LIST ACTIVE [wildmat]\r
300         if(type==1)\r
301         {\r
302                 bool show;\r
303                 std::ostringstream tempstr;\r
304                 BoardList bl;\r
305                 bl.Load();\r
306 \r
307                 SendBufferedLine("215 list of newsgroups follows");\r
308 \r
309                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
310                 {\r
311                         show=true;\r
312                         tempstr.str("");\r
313 \r
314                         // check wilmat match\r
315                         if(arg2!="")\r
316                         {\r
317                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
318                         }\r
319 \r
320                         if(show==true)\r
321                         {\r
322                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");\r
323                                 SendBufferedLine(tempstr.str());\r
324                         }\r
325                 }\r
326 \r
327                 SendBufferedLine(".");\r
328 \r
329         }\r
330         // LIST NEWSGROUPS\r
331         else if(type==2)\r
332         {\r
333                 bool show;\r
334                 std::ostringstream tempstr;\r
335                 BoardList bl;\r
336                 bl.Load();\r
337 \r
338                 SendBufferedLine("215 list of newsgroups follows");\r
339 \r
340                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
341                 {\r
342                         show=true;\r
343                         tempstr.str("");\r
344 \r
345                         // check wilmat match\r
346                         if(arg2!="")\r
347                         {\r
348                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
349                         }\r
350 \r
351                         if(show==true)\r
352                         {\r
353                                 tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();\r
354                                 SendBufferedLine(tempstr.str());\r
355                         }\r
356                 }\r
357 \r
358                 SendBufferedLine(".");\r
359 \r
360         }\r
361         // LIST OVERVIEW.FMT\r
362         else if(type==3)\r
363         {\r
364                 SendBufferedLine("215 Order of fields in overview database.");\r
365                 SendBufferedLine("Subject:");\r
366                 SendBufferedLine("From:");\r
367                 SendBufferedLine("Date:");\r
368                 SendBufferedLine("Message-ID:");\r
369                 SendBufferedLine("References:");\r
370                 SendBufferedLine(":bytes");\r
371                 SendBufferedLine(":lines");\r
372                 SendBufferedLine(".");\r
373         }\r
374         else\r
375         {\r
376                 // unknown arg\r
377                 SendBufferedLine("501 Syntax error");\r
378                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListCommand unhandled LIST variant");\r
379         }\r
380 \r
381         return true;\r
382 }\r
383 \r
384 const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)\r
385 {\r
386 \r
387         std::ostringstream tempstr;\r
388         Board board;\r
389         bool validgroup=false;\r
390         int tempint;\r
391         int lownum=-1;\r
392         int highnum=-1;\r
393 \r
394         // no args and invalid boardid\r
395         if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
396         {\r
397                 SendBufferedLine("412 No newsgroup selected");\r
398         }\r
399         else if(command.m_arguments.size()==0)\r
400         {\r
401                 validgroup=board.Load(m_status.m_boardid);\r
402         }\r
403         else if(command.m_arguments.size()==1)\r
404         {\r
405                 validgroup=board.Load(command.m_arguments[0]);\r
406                 if(validgroup)\r
407                 {\r
408                         lownum=board.GetLowMessageID();\r
409                         highnum=board.GetHighMessageID();\r
410                 }\r
411                 else\r
412                 {\r
413                         SendBufferedLine("411 No such newsgroup");\r
414                 }\r
415         }\r
416         else if(command.m_arguments.size()==2)\r
417         {\r
418                 validgroup=board.Load(command.m_arguments[0]);\r
419                 std::vector<std::string> rangeparts;\r
420                 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
421 \r
422                 if(rangeparts.size()>0)\r
423                 {\r
424                         StringFunctions::Convert(rangeparts[0],lownum);\r
425                 }\r
426                 if(rangeparts.size()>1)\r
427                 {\r
428                         StringFunctions::Convert(rangeparts[1],highnum);\r
429                 }\r
430 \r
431         }\r
432         else\r
433         {\r
434                 // unknown arg\r
435                 SendBufferedLine("501 Syntax error");\r
436                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListGroupCommand unknown arguments");\r
437         }\r
438 \r
439         if(validgroup)\r
440         {\r
441 \r
442                 // set boardid and messageid\r
443                 m_status.m_boardid=board.GetBoardID();\r
444                 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
445 \r
446                 if(lownum==-1)\r
447                 {\r
448                         lownum=board.GetLowMessageID();\r
449                 }\r
450                 if(highnum==-1)\r
451                 {\r
452                         highnum=board.GetHighMessageID();\r
453                 }\r
454 \r
455                 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
456                 SendBufferedLine(tempstr.str());\r
457 \r
458                 MessageList ml;\r
459                 ml.LoadRange(lownum,highnum,board.GetBoardID());\r
460 \r
461                 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
462                 {\r
463                         tempstr.str("");\r
464                         tempstr << (*i).GetMessageID();\r
465 \r
466                         SendBufferedLine(tempstr.str());\r
467                 }\r
468 \r
469                 // end of multi-line response\r
470                 SendBufferedLine(".");\r
471 \r
472         }\r
473 \r
474         return true;\r
475 }\r
476 \r
477 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
478 {\r
479         if(command.m_arguments.size()>0)\r
480         {\r
481                 std::string arg=command.m_arguments[0];\r
482                 StringFunctions::UpperCase(arg,arg);\r
483                 if(arg=="READER")\r
484                 {\r
485                         m_status.m_mode=MODE_READER;\r
486                         if(m_status.m_allowpost==true)\r
487                         {\r
488                                 SendBufferedLine("200 Posting allowed");\r
489                         }\r
490                         else\r
491                         {\r
492                                 SendBufferedLine("201 Posting prohibited");\r
493                         }\r
494                         \r
495                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand set mode to reader");\r
496                 }\r
497                 else\r
498                 {\r
499                         SendBufferedLine("501 Syntax error");\r
500                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
501                 }\r
502         }\r
503         else\r
504         {\r
505                 SendBufferedLine("501 Syntax error");\r
506                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand no argument supplied for MODE");     \r
507         }\r
508 \r
509         return true;\r
510 }\r
511 \r
512 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
513 {\r
514         if(command.m_arguments.size()>=2)\r
515         {\r
516                 DateTime date;\r
517                 int tempint;\r
518                 if(command.m_arguments[0].size()==8)\r
519                 {\r
520                         StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempint);\r
521                         date.SetYear(tempint);\r
522                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
523                         date.SetMonth(tempint);\r
524                         StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempint);\r
525                         date.SetDay(tempint);\r
526                 }\r
527                 else\r
528                 {\r
529                         /*\r
530                         from RFC 3977\r
531                         If the first two digits of the year are not specified\r
532                         (this is supported only for backward compatibility), the year is to\r
533                         be taken from the current century if yy is smaller than or equal to\r
534                         the current year, and the previous century otherwise.\r
535                         */\r
536                         int century;\r
537                         DateTime now;\r
538                         now.SetToGMTime();\r
539                         century=now.GetYear()-(now.GetYear()%100);\r
540 \r
541                         StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempint);\r
542                         tempint<=now.GetYear()-century ? tempint+=century : tempint+=(century-100);\r
543                         \r
544                         //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
545                         \r
546                         date.SetYear(tempint);\r
547                         StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempint);\r
548                         date.SetMonth(tempint);\r
549                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
550                         date.SetDay(tempint);\r
551                 }\r
552 \r
553                 date.Normalize();\r
554 \r
555                 BoardList bl;\r
556 \r
557                 bl.LoadNew(date.Format("%Y-%m-%d %H:%M:%S"));\r
558 \r
559                 SendBufferedLine("231 List of new newsgroups follows");\r
560 \r
561                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
562                 {\r
563                         std::ostringstream tempstr;\r
564                         tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
565                         SendBufferedLine(tempstr.str());\r
566                 }\r
567 \r
568                 SendBufferedLine(".");\r
569 \r
570         }\r
571         else\r
572         {\r
573                 SendBufferedLine("501 Syntax error");\r
574                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleNewGroupsCommand syntax error");\r
575         }\r
576 \r
577         return true;\r
578 \r
579 }\r
580 \r
581 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
582 {\r
583         if(m_status.m_boardid!=-1)\r
584         {\r
585                 if(m_status.m_messageid!=-1)\r
586                 {\r
587                         Message mess;\r
588 \r
589                         if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))\r
590                         {\r
591                                 std::ostringstream tempstr;\r
592 \r
593                                 m_status.m_messageid=mess.GetMessageID();\r
594 \r
595                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
596 \r
597                                 SendBufferedLine(tempstr.str());\r
598 \r
599                         }\r
600                         else\r
601                         {\r
602                                 SendBufferedLine("421 No next article in this group");\r
603                         }\r
604                 }\r
605                 else\r
606                 {\r
607                         SendBufferedLine("420 Current article number is invalid");\r
608                 }\r
609         }\r
610         else\r
611         {\r
612                 SendBufferedLine("412 No newsgroup selected");\r
613         }\r
614 \r
615         return true;\r
616 \r
617 }\r
618 \r
619 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
620 {\r
621         long lowmessageid,highmessageid;\r
622         std::string messageuuid="";\r
623 \r
624         lowmessageid=highmessageid=-2;\r
625 \r
626         if(command.m_arguments.size()==0)\r
627         {\r
628                 lowmessageid=m_status.m_messageid;\r
629                 highmessageid=m_status.m_messageid;\r
630         }\r
631         else\r
632         {\r
633                 // Message-ID\r
634                 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
635                 {\r
636                         messageuuid=command.m_arguments[0];\r
637                         messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
638                         messageuuid=StringFunctions::Replace(messageuuid,">","");\r
639                         // get rid of @ and everything after\r
640                         if(messageuuid.find("@")!=std::string::npos)\r
641                         {\r
642                                 messageuuid.erase(messageuuid.find("@"));\r
643                         }\r
644                 }\r
645                 // single article or range\r
646                 else\r
647                 {\r
648                         // range\r
649                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
650                         {\r
651                                 std::vector<std::string> rangeparts;\r
652                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
653                                 // no upper bound\r
654                                 if(rangeparts.size()>0)\r
655                                 {\r
656                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
657                                         highmessageid=-1;\r
658                                 }\r
659                                 //upper bound\r
660                                 else if(rangeparts.size()>1)\r
661                                 {\r
662                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
663                                 }\r
664                         }\r
665                         // single\r
666                         else\r
667                         {\r
668                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
669                         }\r
670                 }\r
671         }\r
672 \r
673         if(messageuuid!="")\r
674         {\r
675                 Message mess;\r
676                 if(mess.Load(messageuuid))\r
677                 {\r
678                         SendBufferedLine("224 Overview information follows");\r
679                         SendArticleOverInfo(mess);\r
680                         SendBufferedLine(".");\r
681                 }\r
682                 else\r
683                 {\r
684                         SendBufferedLine("423 No such article");\r
685                 }\r
686         }\r
687         else\r
688         {\r
689                 Board bd;\r
690                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
691                 {\r
692                         // single message\r
693                         if(highmessageid==-2)\r
694                         {\r
695                                 Message mess;\r
696                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
697                                 {\r
698                                         SendBufferedLine("224 Overview information follows");\r
699                                         SendArticleOverInfo(mess);\r
700                                         SendBufferedLine(".");\r
701                                 }\r
702                                 else\r
703                                 {\r
704                                         SendBufferedLine("423 No such article in this group");\r
705                                 }\r
706                         }\r
707                         // range with no upper bound\r
708                         else if(highmessageid==-1)\r
709                         {\r
710                                 MessageList ml;\r
711                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
712                                 if(ml.size()>0)\r
713                                 {\r
714                                         SendBufferedLine("224 Overview information follows");\r
715                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
716                                         {\r
717                                                 SendArticleOverInfo((*i));\r
718                                         }\r
719                                         SendBufferedLine(".");\r
720                                 }\r
721                                 else\r
722                                 {\r
723                                         SendBufferedLine("423 Empty range");\r
724                                 }\r
725                         }\r
726                         // range with upper and lower bound\r
727                         else if(highmessageid>=lowmessageid)\r
728                         {\r
729                                 MessageList ml;\r
730                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
731                                 if(ml.size()>0)\r
732                                 {\r
733                                         SendBufferedLine("224 Overview information follows");\r
734                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
735                                         {\r
736                                                 SendArticleOverInfo((*i));\r
737                                         }\r
738                                         SendBufferedLine(".");\r
739                                 }\r
740                                 else\r
741                                 {\r
742                                         SendBufferedLine("423 Empty range");\r
743                                 }\r
744                         }\r
745                         // invalid range\r
746                         else\r
747                         {\r
748                                 SendBufferedLine("423 Empty range");\r
749                         }\r
750                 }\r
751                 else\r
752                 {\r
753                         SendBufferedLine("423 No newsgroup selected");\r
754                 }\r
755         }\r
756 \r
757         return true;\r
758 \r
759 }\r
760 \r
761 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
762 {\r
763         if(m_status.m_allowpost==true)\r
764         {\r
765                 SendBufferedLine("340 Send article to be posted");\r
766                 m_status.m_isposting=true;\r
767         }\r
768         else\r
769         {\r
770                 SendBufferedLine("440 Posting not permitted");\r
771         }\r
772 \r
773         return true;\r
774 }\r
775 \r
776 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
777 {\r
778         Message mess;\r
779 \r
780         if(mess.ParseNNTPMessage(message))\r
781         {\r
782                 mess.StartFreenetInsert();\r
783                 SendBufferedLine("240 Article received OK");\r
784         }\r
785         else\r
786         {\r
787                 SendBufferedLine("441 Posting failed");\r
788         }\r
789 }\r
790 \r
791 void NNTPConnection::HandleReceivedData()\r
792 {\r
793         if(m_status.m_isposting==false)\r
794         {\r
795                 // get end of command line\r
796                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
797                 \r
798                 // we got a command\r
799                 if(endpos!=m_receivebuffer.end())\r
800                 {\r
801                         NNTPCommand command;\r
802                         std::string commandline(m_receivebuffer.begin(),endpos);\r
803 \r
804                         // remove command from receive buffer\r
805                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
806 \r
807                         // remove any leading/trailing whitespace\r
808                         commandline=StringFunctions::TrimWhitespace(commandline);\r
809 \r
810                         // split out command and arguments separated by space or tab\r
811                         StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
812 \r
813                         // command is first element in argument vector\r
814                         command.m_command=command.m_arguments[0];\r
815                         // erase command from argument vector and make it upper case\r
816                         command.m_arguments.erase(command.m_arguments.begin());\r
817                         StringFunctions::UpperCase(command.m_command,command.m_command);\r
818 \r
819                         if(HandleCommand(command)==true)\r
820                         {\r
821                                 \r
822                         }\r
823                         else\r
824                         {\r
825                                 SendBufferedLine("500 Unknown command");\r
826 \r
827                                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
828                         }\r
829 \r
830                 }\r
831 \r
832         }\r
833         else\r
834         {\r
835                 // check for end of post\r
836                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
837 \r
838                 if(endpos!=m_receivebuffer.end())\r
839                 {\r
840                         // get the message\r
841                         std::string message(m_receivebuffer.begin(),endpos);\r
842                         // remove from receive buffer\r
843                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
844 \r
845                         // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
846                         message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
847 \r
848                         HandlePostedMessage(message);\r
849 \r
850                         // message was received, so posting is completed\r
851                         m_status.m_isposting=false;\r
852 \r
853                 }\r
854         }\r
855 }\r
856 \r
857 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
858 {\r
859         SendArticleParts(command);\r
860 \r
861         return true;\r
862 }\r
863 \r
864 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
865 {\r
866         SendBufferedLine("205 Connection Closing");\r
867         SocketSend();\r
868         Disconnect();\r
869         m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::HandleQuitCommand client closed connection");\r
870         return true;\r
871 }\r
872 \r
873 void NNTPConnection::Run()\r
874 {\r
875         struct timeval tv;\r
876         fd_set writefs,readfs;\r
877         int rval;\r
878 \r
879         // seed random number generater for this thread\r
880         srand(time(NULL));\r
881         \r
882         if(m_status.m_allowpost==true)\r
883         {\r
884                 SendBufferedLine("200 Service available, posting allowed");\r
885         }\r
886         else\r
887         {\r
888                 SendBufferedLine("201 Service available, posting prohibited");\r
889         }\r
890 \r
891         do\r
892         {\r
893                 FD_ZERO(&readfs);\r
894                 FD_ZERO(&writefs);\r
895                 \r
896                 FD_SET(m_socket,&readfs);\r
897                 if(m_sendbuffer.size()>0)\r
898                 {\r
899                         FD_SET(m_socket,&writefs);\r
900                 }\r
901                 \r
902                 tv.tv_sec=1;\r
903                 tv.tv_usec=0;\r
904                 \r
905                 rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
906                 \r
907                 if(rval>0)\r
908                 {\r
909                         if(FD_ISSET(m_socket,&readfs))\r
910                         {\r
911                                 SocketReceive();\r
912                                 HandleReceivedData();\r
913                         }\r
914                         if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
915                         {\r
916                                 SocketSend();\r
917                         }\r
918                 }\r
919                 else if(rval==SOCKET_ERROR)\r
920                 {\r
921                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());   \r
922                 }\r
923 \r
924 //      }while(!Disconnected() && !ZThread::Thread::interrupted());\r
925         }while(!Disconnected() && !IsCancelled());\r
926 \r
927         Disconnect();\r
928 \r
929 }\r
930 \r
931 void NNTPConnection::SendArticleOverInfo(Message &message)\r
932 {\r
933         std::string tempval;\r
934         std::string line;\r
935         std::map<long,std::string> references;\r
936 \r
937         StringFunctions::Convert(message.GetMessageID(),tempval);\r
938         line=tempval+"\t";\r
939         line+=message.GetSubject()+"\t";\r
940         line+=message.GetFromName()+"\t";\r
941         line+=message.GetDateTime().Format("%a, %d %b %y %H:%M:%S -0000")+"\t";\r
942         line+=message.GetNNTPArticleID()+"\t";\r
943         references=message.GetInReplyTo();\r
944         if(references.size()>0)\r
945         {\r
946                 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
947                 {\r
948                         if(i!=references.rbegin())\r
949                         {\r
950                                 line+=" ";\r
951                         }\r
952                         line+="<"+(*i).second+"@freenetproject.org>";\r
953                 }\r
954                 line+="\t";\r
955         }\r
956         else\r
957         {\r
958                 line+="\t";\r
959         }\r
960         line+="\t";\r
961 \r
962         SendBufferedLine(line);\r
963 }\r
964 \r
965 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
966 {\r
967         bool sendheaders,sendbody;\r
968         std::string successcode;\r
969 \r
970         if(command.m_command=="ARTICLE")\r
971         {\r
972                 sendheaders=true;\r
973                 sendbody=true;\r
974                 successcode="220";\r
975         }\r
976         else if(command.m_command=="HEAD")\r
977         {\r
978                 sendheaders=true;\r
979                 sendbody=false;\r
980                 successcode="221";\r
981         }\r
982         else if(command.m_command=="BODY")\r
983         {\r
984                 sendheaders=false;\r
985                 sendbody=true;\r
986                 successcode="222";\r
987         }\r
988         else if(command.m_command=="STAT")\r
989         {\r
990                 sendheaders=false;\r
991                 sendbody=false;\r
992                 successcode="223";\r
993         }\r
994 \r
995         Message message;\r
996         int messageid=m_status.m_messageid;\r
997         std::string articleid="";\r
998         int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
999 \r
1000         if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
1001         {\r
1002                 if(command.m_arguments[0].find("<")==std::string::npos)\r
1003                 {\r
1004                         StringFunctions::Convert(command.m_arguments[0],messageid);\r
1005                         message.Load(messageid,m_status.m_boardid);\r
1006                         m_status.m_messageid=message.GetMessageID();\r
1007                         type=1;\r
1008                 }\r
1009                 else\r
1010                 {\r
1011                         articleid=command.m_arguments[0];\r
1012                         message.Load(articleid);\r
1013                         type=2;\r
1014                 }\r
1015         }\r
1016         else\r
1017         {\r
1018                 message.Load(m_status.m_messageid,m_status.m_boardid);\r
1019         }\r
1020 \r
1021         switch(type)\r
1022         {\r
1023         case 0:\r
1024                 if(m_status.m_boardid!=-1)\r
1025                 {\r
1026                         if(m_status.m_messageid!=-1)\r
1027                         {\r
1028                                 std::ostringstream tempstr;\r
1029                                 std::string article;\r
1030                                 if(sendheaders&&sendbody)\r
1031                                 {\r
1032                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1033                                 }\r
1034                                 else if(sendheaders && !sendbody)\r
1035                                 {\r
1036                                         article=message.GetNNTPHeaders();\r
1037                                         // strip off final \r\n from headers\r
1038                                         if(article.rfind("\r\n")==article.size()-2)\r
1039                                         {\r
1040                                                 article.erase(article.size()-2);\r
1041                                         }\r
1042                                 }\r
1043                                 else\r
1044                                 {\r
1045                                         article=message.GetNNTPBody();\r
1046                                 }\r
1047                                 // dot stuff article\r
1048                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1049 \r
1050                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1051 \r
1052                                 SendBufferedLine(tempstr.str());\r
1053                                 if(sendheaders || sendbody)\r
1054                                 {\r
1055                                         SendBufferedLine(article);\r
1056                                         SendBufferedLine(".");\r
1057                                 }\r
1058 \r
1059                         }\r
1060                         else\r
1061                         {\r
1062                                 SendBufferedLine("420 Current article number is invalid");\r
1063                         }\r
1064                 }\r
1065                 else\r
1066                 {\r
1067                         SendBufferedLine("412 No newsgroup selected");\r
1068                 }\r
1069                 break;\r
1070         case 1:\r
1071                 if(m_status.m_boardid!=-1)\r
1072                 {\r
1073                         if(message.GetMessageID()!=-1)\r
1074                         {\r
1075                                 std::ostringstream tempstr;\r
1076                                 std::string article;\r
1077                                 if(sendheaders&&sendbody)\r
1078                                 {\r
1079                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1080                                 }\r
1081                                 else if(sendheaders && !sendbody)\r
1082                                 {\r
1083                                         article=message.GetNNTPHeaders();\r
1084                                         // strip off final \r\n from headers\r
1085                                         if(article.rfind("\r\n")==article.size()-2)\r
1086                                         {\r
1087                                                 article.erase(article.size()-2);\r
1088                                         }\r
1089                                 }\r
1090                                 else\r
1091                                 {\r
1092                                         article=message.GetNNTPBody();\r
1093                                 }\r
1094                                 // dot stuff article\r
1095                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1096 \r
1097                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1098 \r
1099                                 SendBufferedLine(tempstr.str());\r
1100                                 if(sendheaders || sendbody)\r
1101                                 {\r
1102                                         SendBufferedLine(article);\r
1103                                         SendBufferedLine(".");\r
1104                                 }\r
1105                         }\r
1106                         else\r
1107                         {\r
1108                                 SendBufferedLine("423 No article with that number");\r
1109                         }\r
1110                 }\r
1111                 else\r
1112                 {\r
1113                         SendBufferedLine("412 No newsgroup selected");\r
1114                 }\r
1115                 break;\r
1116         case 2:\r
1117                 if(message.GetMessageID()!=-1)\r
1118                 {\r
1119                         std::string article;\r
1120                         if(sendheaders&&sendbody)\r
1121                         {\r
1122                                 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1123                         }\r
1124                         else if(sendheaders && !sendbody)\r
1125                         {\r
1126                                 article=message.GetNNTPHeaders();\r
1127                                 // strip off final \r\n from headers\r
1128                                 if(article.rfind("\r\n")==article.size()-2)\r
1129                                 {\r
1130                                         article.erase(article.size()-2);\r
1131                                 }\r
1132                         }\r
1133                         else\r
1134                         {\r
1135                                 article=message.GetNNTPBody();\r
1136                         }\r
1137                         // dot stuff article\r
1138                         article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1139 \r
1140                         SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
1141                         if(sendheaders || sendbody)\r
1142                         {\r
1143                                 SendBufferedLine(article);\r
1144                                 SendBufferedLine(".");\r
1145                         }\r
1146                 }\r
1147                 else\r
1148                 {\r
1149                         SendBufferedLine("430 No article with that message-id");\r
1150                 }\r
1151                 break;\r
1152         }\r
1153 \r
1154 }\r
1155 \r
1156 void NNTPConnection::SendBuffered(const std::string &data)\r
1157 {\r
1158         m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
1159 }\r
1160 \r
1161 void NNTPConnection::SocketReceive()\r
1162 {\r
1163         int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
1164         if(rval>0)\r
1165         {\r
1166                 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
1167         }\r
1168         else if(rval==0)\r
1169         {\r
1170                 Disconnect();\r
1171                 m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::SocketReceive remote host closed connection");\r
1172         }\r
1173         else if(rval==-1)\r
1174         {\r
1175                 std::string errnostr;\r
1176                 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1177                 // error on receive - close the connection\r
1178                 Disconnect();\r
1179                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1180         }\r
1181 }\r
1182 \r
1183 void NNTPConnection::SocketSend()\r
1184 {\r
1185         if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
1186         {\r
1187                 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
1188                 if(rval>0)\r
1189                 {       \r
1190                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
1191                 }\r
1192                 else if(rval==-1)\r
1193                 {\r
1194                         std::string errnostr;\r
1195                         StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1196                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1197                 }\r
1198         }\r
1199 }\r