3fffad7d312c4e12194da23d85e0a94949227c25
[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 lownum=-1;\r
391         int highnum=-1;\r
392 \r
393         // no args and invalid boardid\r
394         if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
395         {\r
396                 SendBufferedLine("412 No newsgroup selected");\r
397         }\r
398         else if(command.m_arguments.size()==0)\r
399         {\r
400                 validgroup=board.Load(m_status.m_boardid);\r
401         }\r
402         else if(command.m_arguments.size()==1)\r
403         {\r
404                 validgroup=board.Load(command.m_arguments[0]);\r
405                 if(validgroup)\r
406                 {\r
407                         lownum=board.GetLowMessageID();\r
408                         highnum=board.GetHighMessageID();\r
409                 }\r
410                 else\r
411                 {\r
412                         SendBufferedLine("411 No such newsgroup");\r
413                 }\r
414         }\r
415         else if(command.m_arguments.size()==2)\r
416         {\r
417                 validgroup=board.Load(command.m_arguments[0]);\r
418                 std::vector<std::string> rangeparts;\r
419                 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
420 \r
421                 if(rangeparts.size()>0)\r
422                 {\r
423                         StringFunctions::Convert(rangeparts[0],lownum);\r
424                 }\r
425                 if(rangeparts.size()>1)\r
426                 {\r
427                         StringFunctions::Convert(rangeparts[1],highnum);\r
428                 }\r
429 \r
430         }\r
431         else\r
432         {\r
433                 // unknown arg\r
434                 SendBufferedLine("501 Syntax error");\r
435                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListGroupCommand unknown arguments");\r
436         }\r
437 \r
438         if(validgroup)\r
439         {\r
440 \r
441                 // set boardid and messageid\r
442                 m_status.m_boardid=board.GetBoardID();\r
443                 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
444 \r
445                 if(lownum==-1)\r
446                 {\r
447                         lownum=board.GetLowMessageID();\r
448                 }\r
449                 if(highnum==-1)\r
450                 {\r
451                         highnum=board.GetHighMessageID();\r
452                 }\r
453 \r
454                 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
455                 SendBufferedLine(tempstr.str());\r
456 \r
457                 MessageList ml;\r
458                 ml.LoadRange(lownum,highnum,board.GetBoardID());\r
459 \r
460                 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
461                 {\r
462                         tempstr.str("");\r
463                         tempstr << (*i).GetMessageID();\r
464 \r
465                         SendBufferedLine(tempstr.str());\r
466                 }\r
467 \r
468                 // end of multi-line response\r
469                 SendBufferedLine(".");\r
470 \r
471         }\r
472 \r
473         return true;\r
474 }\r
475 \r
476 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
477 {\r
478         if(command.m_arguments.size()>0)\r
479         {\r
480                 std::string arg=command.m_arguments[0];\r
481                 StringFunctions::UpperCase(arg,arg);\r
482                 if(arg=="READER")\r
483                 {\r
484                         m_status.m_mode=MODE_READER;\r
485                         if(m_status.m_allowpost==true)\r
486                         {\r
487                                 SendBufferedLine("200 Posting allowed");\r
488                         }\r
489                         else\r
490                         {\r
491                                 SendBufferedLine("201 Posting prohibited");\r
492                         }\r
493                         \r
494                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand set mode to reader");\r
495                 }\r
496                 else\r
497                 {\r
498                         SendBufferedLine("501 Syntax error");\r
499                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
500                 }\r
501         }\r
502         else\r
503         {\r
504                 SendBufferedLine("501 Syntax error");\r
505                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand no argument supplied for MODE");     \r
506         }\r
507 \r
508         return true;\r
509 }\r
510 \r
511 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
512 {\r
513         if(command.m_arguments.size()>=2)\r
514         {\r
515                 DateTime date;\r
516                 int tempint;\r
517                 if(command.m_arguments[0].size()==8)\r
518                 {\r
519                         StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempint);\r
520                         date.SetYear(tempint);\r
521                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
522                         date.SetMonth(tempint);\r
523                         StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempint);\r
524                         date.SetDay(tempint);\r
525                 }\r
526                 else\r
527                 {\r
528                         /*\r
529                         from RFC 3977\r
530                         If the first two digits of the year are not specified\r
531                         (this is supported only for backward compatibility), the year is to\r
532                         be taken from the current century if yy is smaller than or equal to\r
533                         the current year, and the previous century otherwise.\r
534                         */\r
535                         int century;\r
536                         DateTime now;\r
537                         now.SetToGMTime();\r
538                         century=now.GetYear()-(now.GetYear()%100);\r
539 \r
540                         StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempint);\r
541                         tempint<=now.GetYear()-century ? tempint+=century : tempint+=(century-100);\r
542                         \r
543                         //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
544                         \r
545                         date.SetYear(tempint);\r
546                         StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempint);\r
547                         date.SetMonth(tempint);\r
548                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
549                         date.SetDay(tempint);\r
550                 }\r
551 \r
552                 date.Normalize();\r
553 \r
554                 BoardList bl;\r
555 \r
556                 bl.LoadNew(date.Format("%Y-%m-%d %H:%M:%S"));\r
557 \r
558                 SendBufferedLine("231 List of new newsgroups follows");\r
559 \r
560                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
561                 {\r
562                         std::ostringstream tempstr;\r
563                         tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
564                         SendBufferedLine(tempstr.str());\r
565                 }\r
566 \r
567                 SendBufferedLine(".");\r
568 \r
569         }\r
570         else\r
571         {\r
572                 SendBufferedLine("501 Syntax error");\r
573                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleNewGroupsCommand syntax error");\r
574         }\r
575 \r
576         return true;\r
577 \r
578 }\r
579 \r
580 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
581 {\r
582         if(m_status.m_boardid!=-1)\r
583         {\r
584                 if(m_status.m_messageid!=-1)\r
585                 {\r
586                         Message mess;\r
587 \r
588                         if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))\r
589                         {\r
590                                 std::ostringstream tempstr;\r
591 \r
592                                 m_status.m_messageid=mess.GetMessageID();\r
593 \r
594                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
595 \r
596                                 SendBufferedLine(tempstr.str());\r
597 \r
598                         }\r
599                         else\r
600                         {\r
601                                 SendBufferedLine("421 No next article in this group");\r
602                         }\r
603                 }\r
604                 else\r
605                 {\r
606                         SendBufferedLine("420 Current article number is invalid");\r
607                 }\r
608         }\r
609         else\r
610         {\r
611                 SendBufferedLine("412 No newsgroup selected");\r
612         }\r
613 \r
614         return true;\r
615 \r
616 }\r
617 \r
618 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
619 {\r
620         long lowmessageid,highmessageid;\r
621         std::string messageuuid="";\r
622 \r
623         lowmessageid=highmessageid=-2;\r
624 \r
625         if(command.m_arguments.size()==0)\r
626         {\r
627                 lowmessageid=m_status.m_messageid;\r
628                 highmessageid=m_status.m_messageid;\r
629         }\r
630         else\r
631         {\r
632                 // Message-ID\r
633                 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
634                 {\r
635                         messageuuid=command.m_arguments[0];\r
636                         messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
637                         messageuuid=StringFunctions::Replace(messageuuid,">","");\r
638                         // get rid of @ and everything after\r
639                         if(messageuuid.find("@")!=std::string::npos)\r
640                         {\r
641                                 messageuuid.erase(messageuuid.find("@"));\r
642                         }\r
643                 }\r
644                 // single article or range\r
645                 else\r
646                 {\r
647                         // range\r
648                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
649                         {\r
650                                 std::vector<std::string> rangeparts;\r
651                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
652                                 // no upper bound\r
653                                 if(rangeparts.size()>0)\r
654                                 {\r
655                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
656                                         highmessageid=-1;\r
657                                 }\r
658                                 //upper bound\r
659                                 else if(rangeparts.size()>1)\r
660                                 {\r
661                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
662                                 }\r
663                         }\r
664                         // single\r
665                         else\r
666                         {\r
667                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
668                         }\r
669                 }\r
670         }\r
671 \r
672         if(messageuuid!="")\r
673         {\r
674                 Message mess;\r
675                 if(mess.Load(messageuuid))\r
676                 {\r
677                         SendBufferedLine("224 Overview information follows");\r
678                         SendArticleOverInfo(mess);\r
679                         SendBufferedLine(".");\r
680                 }\r
681                 else\r
682                 {\r
683                         SendBufferedLine("423 No such article");\r
684                 }\r
685         }\r
686         else\r
687         {\r
688                 Board bd;\r
689                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
690                 {\r
691                         // single message\r
692                         if(highmessageid==-2)\r
693                         {\r
694                                 Message mess;\r
695                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
696                                 {\r
697                                         SendBufferedLine("224 Overview information follows");\r
698                                         SendArticleOverInfo(mess);\r
699                                         SendBufferedLine(".");\r
700                                 }\r
701                                 else\r
702                                 {\r
703                                         SendBufferedLine("423 No such article in this group");\r
704                                 }\r
705                         }\r
706                         // range with no upper bound\r
707                         else if(highmessageid==-1)\r
708                         {\r
709                                 MessageList ml;\r
710                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
711                                 if(ml.size()>0)\r
712                                 {\r
713                                         SendBufferedLine("224 Overview information follows");\r
714                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
715                                         {\r
716                                                 SendArticleOverInfo((*i));\r
717                                         }\r
718                                         SendBufferedLine(".");\r
719                                 }\r
720                                 else\r
721                                 {\r
722                                         SendBufferedLine("423 Empty range");\r
723                                 }\r
724                         }\r
725                         // range with upper and lower bound\r
726                         else if(highmessageid>=lowmessageid)\r
727                         {\r
728                                 MessageList ml;\r
729                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
730                                 if(ml.size()>0)\r
731                                 {\r
732                                         SendBufferedLine("224 Overview information follows");\r
733                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
734                                         {\r
735                                                 SendArticleOverInfo((*i));\r
736                                         }\r
737                                         SendBufferedLine(".");\r
738                                 }\r
739                                 else\r
740                                 {\r
741                                         SendBufferedLine("423 Empty range");\r
742                                 }\r
743                         }\r
744                         // invalid range\r
745                         else\r
746                         {\r
747                                 SendBufferedLine("423 Empty range");\r
748                         }\r
749                 }\r
750                 else\r
751                 {\r
752                         SendBufferedLine("423 No newsgroup selected");\r
753                 }\r
754         }\r
755 \r
756         return true;\r
757 \r
758 }\r
759 \r
760 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
761 {\r
762         if(m_status.m_allowpost==true)\r
763         {\r
764                 SendBufferedLine("340 Send article to be posted");\r
765                 m_status.m_isposting=true;\r
766         }\r
767         else\r
768         {\r
769                 SendBufferedLine("440 Posting not permitted");\r
770         }\r
771 \r
772         return true;\r
773 }\r
774 \r
775 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
776 {\r
777         Message mess;\r
778 \r
779         if(mess.ParseNNTPMessage(message))\r
780         {\r
781                 if(mess.PostedToAdministrationBoard()==true)\r
782                 {\r
783                         mess.HandleAdministrationMessage();\r
784                 }\r
785                 else\r
786                 {\r
787                         mess.StartFreenetInsert();\r
788                 }\r
789                 SendBufferedLine("240 Article received OK");\r
790         }\r
791         else\r
792         {\r
793                 SendBufferedLine("441 Posting failed");\r
794         }\r
795 }\r
796 \r
797 void NNTPConnection::HandleReceivedData()\r
798 {\r
799         if(m_status.m_isposting==false)\r
800         {\r
801                 // get end of command line\r
802                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
803                 \r
804                 // we got a command\r
805                 if(endpos!=m_receivebuffer.end())\r
806                 {\r
807                         NNTPCommand command;\r
808                         std::string commandline(m_receivebuffer.begin(),endpos);\r
809 \r
810                         // remove command from receive buffer\r
811                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
812 \r
813                         // remove any leading/trailing whitespace\r
814                         commandline=StringFunctions::TrimWhitespace(commandline);\r
815 \r
816                         // split out command and arguments separated by space or tab\r
817                         StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
818 \r
819                         // command is first element in argument vector\r
820                         command.m_command=command.m_arguments[0];\r
821                         // erase command from argument vector and make it upper case\r
822                         command.m_arguments.erase(command.m_arguments.begin());\r
823                         StringFunctions::UpperCase(command.m_command,command.m_command);\r
824 \r
825                         if(HandleCommand(command)==true)\r
826                         {\r
827                                 \r
828                         }\r
829                         else\r
830                         {\r
831                                 SendBufferedLine("500 Unknown command");\r
832 \r
833                                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
834                         }\r
835 \r
836                 }\r
837 \r
838         }\r
839         else\r
840         {\r
841                 // check for end of post\r
842                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
843 \r
844                 if(endpos!=m_receivebuffer.end())\r
845                 {\r
846                         // get the message\r
847                         std::string message(m_receivebuffer.begin(),endpos);\r
848                         // remove from receive buffer\r
849                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
850 \r
851                         // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
852                         message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
853 \r
854                         HandlePostedMessage(message);\r
855 \r
856                         // message was received, so posting is completed\r
857                         m_status.m_isposting=false;\r
858 \r
859                 }\r
860         }\r
861 }\r
862 \r
863 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
864 {\r
865         SendArticleParts(command);\r
866 \r
867         return true;\r
868 }\r
869 \r
870 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
871 {\r
872         SendBufferedLine("205 Connection Closing");\r
873         SocketSend();\r
874         Disconnect();\r
875         m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::HandleQuitCommand client closed connection");\r
876         return true;\r
877 }\r
878 \r
879 void NNTPConnection::Run()\r
880 {\r
881         struct timeval tv;\r
882         fd_set writefs,readfs;\r
883         int rval;\r
884 \r
885         // seed random number generater for this thread\r
886         srand(time(NULL));\r
887         \r
888         if(m_status.m_allowpost==true)\r
889         {\r
890                 SendBufferedLine("200 Service available, posting allowed");\r
891         }\r
892         else\r
893         {\r
894                 SendBufferedLine("201 Service available, posting prohibited");\r
895         }\r
896 \r
897         do\r
898         {\r
899                 FD_ZERO(&readfs);\r
900                 FD_ZERO(&writefs);\r
901                 \r
902                 FD_SET(m_socket,&readfs);\r
903                 if(m_sendbuffer.size()>0)\r
904                 {\r
905                         FD_SET(m_socket,&writefs);\r
906                 }\r
907                 \r
908                 tv.tv_sec=1;\r
909                 tv.tv_usec=0;\r
910                 \r
911                 rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
912                 \r
913                 if(rval>0)\r
914                 {\r
915                         if(FD_ISSET(m_socket,&readfs))\r
916                         {\r
917                                 SocketReceive();\r
918                                 HandleReceivedData();\r
919                         }\r
920                         if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
921                         {\r
922                                 SocketSend();\r
923                         }\r
924                 }\r
925                 else if(rval==SOCKET_ERROR)\r
926                 {\r
927                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());   \r
928                 }\r
929 \r
930 //      }while(!Disconnected() && !ZThread::Thread::interrupted());\r
931         }while(!Disconnected() && !IsCancelled());\r
932 \r
933         Disconnect();\r
934 \r
935 }\r
936 \r
937 void NNTPConnection::SendArticleOverInfo(Message &message)\r
938 {\r
939         std::string tempval;\r
940         std::string line;\r
941         std::map<long,std::string> references;\r
942 \r
943         StringFunctions::Convert(message.GetMessageID(),tempval);\r
944         line=tempval+"\t";\r
945         line+=message.GetSubject()+"\t";\r
946         line+=message.GetFromName()+"\t";\r
947         line+=message.GetDateTime().Format("%a, %d %b %y %H:%M:%S -0000")+"\t";\r
948         line+=message.GetNNTPArticleID()+"\t";\r
949         references=message.GetInReplyTo();\r
950         if(references.size()>0)\r
951         {\r
952                 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
953                 {\r
954                         if(i!=references.rbegin())\r
955                         {\r
956                                 line+=" ";\r
957                         }\r
958                         line+="<"+(*i).second+"@freenetproject.org>";\r
959                 }\r
960                 line+="\t";\r
961         }\r
962         else\r
963         {\r
964                 line+="\t";\r
965         }\r
966         line+="\t";\r
967 \r
968         SendBufferedLine(line);\r
969 }\r
970 \r
971 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
972 {\r
973         bool sendheaders,sendbody;\r
974         std::string successcode;\r
975 \r
976         if(command.m_command=="ARTICLE")\r
977         {\r
978                 sendheaders=true;\r
979                 sendbody=true;\r
980                 successcode="220";\r
981         }\r
982         else if(command.m_command=="HEAD")\r
983         {\r
984                 sendheaders=true;\r
985                 sendbody=false;\r
986                 successcode="221";\r
987         }\r
988         else if(command.m_command=="BODY")\r
989         {\r
990                 sendheaders=false;\r
991                 sendbody=true;\r
992                 successcode="222";\r
993         }\r
994         else if(command.m_command=="STAT")\r
995         {\r
996                 sendheaders=false;\r
997                 sendbody=false;\r
998                 successcode="223";\r
999         }\r
1000 \r
1001         Message message;\r
1002         int messageid=m_status.m_messageid;\r
1003         std::string articleid="";\r
1004         int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
1005 \r
1006         if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
1007         {\r
1008                 if(command.m_arguments[0].find("<")==std::string::npos)\r
1009                 {\r
1010                         StringFunctions::Convert(command.m_arguments[0],messageid);\r
1011                         message.Load(messageid,m_status.m_boardid);\r
1012                         m_status.m_messageid=message.GetMessageID();\r
1013                         type=1;\r
1014                 }\r
1015                 else\r
1016                 {\r
1017                         articleid=command.m_arguments[0];\r
1018                         message.Load(articleid);\r
1019                         type=2;\r
1020                 }\r
1021         }\r
1022         else\r
1023         {\r
1024                 message.Load(m_status.m_messageid,m_status.m_boardid);\r
1025         }\r
1026 \r
1027         switch(type)\r
1028         {\r
1029         case 0:\r
1030                 if(m_status.m_boardid!=-1)\r
1031                 {\r
1032                         if(m_status.m_messageid!=-1)\r
1033                         {\r
1034                                 std::ostringstream tempstr;\r
1035                                 std::string article;\r
1036                                 if(sendheaders&&sendbody)\r
1037                                 {\r
1038                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1039                                 }\r
1040                                 else if(sendheaders && !sendbody)\r
1041                                 {\r
1042                                         article=message.GetNNTPHeaders();\r
1043                                         // strip off final \r\n from headers\r
1044                                         if(article.rfind("\r\n")==article.size()-2)\r
1045                                         {\r
1046                                                 article.erase(article.size()-2);\r
1047                                         }\r
1048                                 }\r
1049                                 else\r
1050                                 {\r
1051                                         article=message.GetNNTPBody();\r
1052                                 }\r
1053                                 // dot stuff article\r
1054                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1055 \r
1056                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1057 \r
1058                                 SendBufferedLine(tempstr.str());\r
1059                                 if(sendheaders || sendbody)\r
1060                                 {\r
1061                                         SendBufferedLine(article);\r
1062                                         SendBufferedLine(".");\r
1063                                 }\r
1064 \r
1065                         }\r
1066                         else\r
1067                         {\r
1068                                 SendBufferedLine("420 Current article number is invalid");\r
1069                         }\r
1070                 }\r
1071                 else\r
1072                 {\r
1073                         SendBufferedLine("412 No newsgroup selected");\r
1074                 }\r
1075                 break;\r
1076         case 1:\r
1077                 if(m_status.m_boardid!=-1)\r
1078                 {\r
1079                         if(message.GetMessageID()!=-1)\r
1080                         {\r
1081                                 std::ostringstream tempstr;\r
1082                                 std::string article;\r
1083                                 if(sendheaders&&sendbody)\r
1084                                 {\r
1085                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1086                                 }\r
1087                                 else if(sendheaders && !sendbody)\r
1088                                 {\r
1089                                         article=message.GetNNTPHeaders();\r
1090                                         // strip off final \r\n from headers\r
1091                                         if(article.rfind("\r\n")==article.size()-2)\r
1092                                         {\r
1093                                                 article.erase(article.size()-2);\r
1094                                         }\r
1095                                 }\r
1096                                 else\r
1097                                 {\r
1098                                         article=message.GetNNTPBody();\r
1099                                 }\r
1100                                 // dot stuff article\r
1101                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1102 \r
1103                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1104 \r
1105                                 SendBufferedLine(tempstr.str());\r
1106                                 if(sendheaders || sendbody)\r
1107                                 {\r
1108                                         SendBufferedLine(article);\r
1109                                         SendBufferedLine(".");\r
1110                                 }\r
1111                         }\r
1112                         else\r
1113                         {\r
1114                                 SendBufferedLine("423 No article with that number");\r
1115                         }\r
1116                 }\r
1117                 else\r
1118                 {\r
1119                         SendBufferedLine("412 No newsgroup selected");\r
1120                 }\r
1121                 break;\r
1122         case 2:\r
1123                 if(message.GetMessageID()!=-1)\r
1124                 {\r
1125                         std::string article;\r
1126                         if(sendheaders&&sendbody)\r
1127                         {\r
1128                                 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1129                         }\r
1130                         else if(sendheaders && !sendbody)\r
1131                         {\r
1132                                 article=message.GetNNTPHeaders();\r
1133                                 // strip off final \r\n from headers\r
1134                                 if(article.rfind("\r\n")==article.size()-2)\r
1135                                 {\r
1136                                         article.erase(article.size()-2);\r
1137                                 }\r
1138                         }\r
1139                         else\r
1140                         {\r
1141                                 article=message.GetNNTPBody();\r
1142                         }\r
1143                         // dot stuff article\r
1144                         article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1145 \r
1146                         SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
1147                         if(sendheaders || sendbody)\r
1148                         {\r
1149                                 SendBufferedLine(article);\r
1150                                 SendBufferedLine(".");\r
1151                         }\r
1152                 }\r
1153                 else\r
1154                 {\r
1155                         SendBufferedLine("430 No article with that message-id");\r
1156                 }\r
1157                 break;\r
1158         }\r
1159 \r
1160 }\r
1161 \r
1162 void NNTPConnection::SendBuffered(const std::string &data)\r
1163 {\r
1164         m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
1165 }\r
1166 \r
1167 void NNTPConnection::SocketReceive()\r
1168 {\r
1169         int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
1170         if(rval>0)\r
1171         {\r
1172                 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
1173         }\r
1174         else if(rval==0)\r
1175         {\r
1176                 Disconnect();\r
1177                 m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::SocketReceive remote host closed connection");\r
1178         }\r
1179         else if(rval==-1)\r
1180         {\r
1181                 std::string errnostr;\r
1182                 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1183                 // error on receive - close the connection\r
1184                 Disconnect();\r
1185                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1186         }\r
1187 }\r
1188 \r
1189 void NNTPConnection::SocketSend()\r
1190 {\r
1191         if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
1192         {\r
1193                 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
1194                 if(rval>0)\r
1195                 {       \r
1196                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
1197                 }\r
1198                 else if(rval==-1)\r
1199                 {\r
1200                         std::string errnostr;\r
1201                         StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1202                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1203                 }\r
1204         }\r
1205 }\r