version 0.1.6
[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                 }\r
640                 // single article or range\r
641                 else\r
642                 {\r
643                         // range\r
644                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
645                         {\r
646                                 std::vector<std::string> rangeparts;\r
647                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
648                                 // no upper bound\r
649                                 if(rangeparts.size()>0)\r
650                                 {\r
651                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
652                                         highmessageid=-1;\r
653                                 }\r
654                                 //upper bound\r
655                                 else if(rangeparts.size()>1)\r
656                                 {\r
657                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
658                                 }\r
659                         }\r
660                         // single\r
661                         else\r
662                         {\r
663                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
664                         }\r
665                 }\r
666         }\r
667 \r
668         if(messageuuid!="")\r
669         {\r
670                 Message mess;\r
671                 if(mess.Load(messageuuid))\r
672                 {\r
673                         SendBufferedLine("224 Overview information follows");\r
674                         SendArticleOverInfo(mess);\r
675                         SendBufferedLine(".");\r
676                 }\r
677                 else\r
678                 {\r
679                         SendBufferedLine("423 No such article");\r
680                 }\r
681         }\r
682         else\r
683         {\r
684                 Board bd;\r
685                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
686                 {\r
687                         // single message\r
688                         if(highmessageid==-2)\r
689                         {\r
690                                 Message mess;\r
691                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
692                                 {\r
693                                         SendBufferedLine("224 Overview information follows");\r
694                                         SendArticleOverInfo(mess);\r
695                                         SendBufferedLine(".");\r
696                                 }\r
697                                 else\r
698                                 {\r
699                                         SendBufferedLine("423 No such article in this group");\r
700                                 }\r
701                         }\r
702                         // range with no upper bound\r
703                         else if(highmessageid==-1)\r
704                         {\r
705                                 MessageList ml;\r
706                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
707                                 if(ml.size()>0)\r
708                                 {\r
709                                         SendBufferedLine("224 Overview information follows");\r
710                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
711                                         {\r
712                                                 SendArticleOverInfo((*i));\r
713                                         }\r
714                                         SendBufferedLine(".");\r
715                                 }\r
716                                 else\r
717                                 {\r
718                                         SendBufferedLine("423 Empty range");\r
719                                 }\r
720                         }\r
721                         // range with upper and lower bound\r
722                         else if(highmessageid>=lowmessageid)\r
723                         {\r
724                                 MessageList ml;\r
725                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
726                                 if(ml.size()>0)\r
727                                 {\r
728                                         SendBufferedLine("224 Overview information follows");\r
729                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
730                                         {\r
731                                                 SendArticleOverInfo((*i));\r
732                                         }\r
733                                         SendBufferedLine(".");\r
734                                 }\r
735                                 else\r
736                                 {\r
737                                         SendBufferedLine("423 Empty range");\r
738                                 }\r
739                         }\r
740                         // invalid range\r
741                         else\r
742                         {\r
743                                 SendBufferedLine("423 Empty range");\r
744                         }\r
745                 }\r
746                 else\r
747                 {\r
748                         SendBufferedLine("423 No newsgroup selected");\r
749                 }\r
750         }\r
751 \r
752         return true;\r
753 \r
754 }\r
755 \r
756 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
757 {\r
758         if(m_status.m_allowpost==true)\r
759         {\r
760                 SendBufferedLine("340 Send article to be posted");\r
761                 m_status.m_isposting=true;\r
762         }\r
763         else\r
764         {\r
765                 SendBufferedLine("440 Posting not permitted");\r
766         }\r
767 \r
768         return true;\r
769 }\r
770 \r
771 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
772 {\r
773         Message mess;\r
774 \r
775         if(mess.ParseNNTPMessage(message))\r
776         {\r
777                 mess.StartFreenetInsert();\r
778                 SendBufferedLine("240 Article received OK");\r
779         }\r
780         else\r
781         {\r
782                 SendBufferedLine("441 Posting failed");\r
783         }\r
784 }\r
785 \r
786 void NNTPConnection::HandleReceivedData()\r
787 {\r
788         if(m_status.m_isposting==false)\r
789         {\r
790                 // get end of command line\r
791                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
792                 \r
793                 // we got a command\r
794                 if(endpos!=m_receivebuffer.end())\r
795                 {\r
796                         NNTPCommand command;\r
797                         std::string commandline(m_receivebuffer.begin(),endpos);\r
798 \r
799                         // remove command from receive buffer\r
800                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
801 \r
802                         // remove any leading/trailing whitespace\r
803                         commandline=StringFunctions::TrimWhitespace(commandline);\r
804 \r
805                         // split out command and arguments separated by space or tab\r
806                         StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
807 \r
808                         // command is first element in argument vector\r
809                         command.m_command=command.m_arguments[0];\r
810                         // erase command from argument vector and make it upper case\r
811                         command.m_arguments.erase(command.m_arguments.begin());\r
812                         StringFunctions::UpperCase(command.m_command,command.m_command);\r
813 \r
814                         if(HandleCommand(command)==true)\r
815                         {\r
816                                 \r
817                         }\r
818                         else\r
819                         {\r
820                                 SendBufferedLine("500 Unknown command");\r
821 \r
822                                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
823                         }\r
824 \r
825                 }\r
826 \r
827         }\r
828         else\r
829         {\r
830                 // check for end of post\r
831                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
832 \r
833                 if(endpos!=m_receivebuffer.end())\r
834                 {\r
835                         // get the message\r
836                         std::string message(m_receivebuffer.begin(),endpos);\r
837                         // remove from receive buffer\r
838                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
839 \r
840                         // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
841                         message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
842 \r
843                         HandlePostedMessage(message);\r
844 \r
845                         // message was received, so posting is completed\r
846                         m_status.m_isposting=false;\r
847 \r
848                 }\r
849         }\r
850 }\r
851 \r
852 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
853 {\r
854         SendArticleParts(command);\r
855 \r
856         return true;\r
857 }\r
858 \r
859 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
860 {\r
861         SendBufferedLine("205 Connection Closing");\r
862         SocketSend();\r
863         Disconnect();\r
864         m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::HandleQuitCommand client closed connection");\r
865         return true;\r
866 }\r
867 \r
868 void NNTPConnection::Run()\r
869 {\r
870         struct timeval tv;\r
871         fd_set writefs,readfs;\r
872         int rval;\r
873 \r
874         // seed random number generater for this thread\r
875         srand(time(NULL));\r
876         \r
877         if(m_status.m_allowpost==true)\r
878         {\r
879                 SendBufferedLine("200 Service available, posting allowed");\r
880         }\r
881         else\r
882         {\r
883                 SendBufferedLine("201 Service available, posting prohibited");\r
884         }\r
885 \r
886         do\r
887         {\r
888                 FD_ZERO(&readfs);\r
889                 FD_ZERO(&writefs);\r
890                 \r
891                 FD_SET(m_socket,&readfs);\r
892                 if(m_sendbuffer.size()>0)\r
893                 {\r
894                         FD_SET(m_socket,&writefs);\r
895                 }\r
896                 \r
897                 tv.tv_sec=1;\r
898                 tv.tv_usec=0;\r
899                 \r
900                 rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
901                 \r
902                 if(rval>0)\r
903                 {\r
904                         if(FD_ISSET(m_socket,&readfs))\r
905                         {\r
906                                 SocketReceive();\r
907                                 HandleReceivedData();\r
908                         }\r
909                         if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
910                         {\r
911                                 SocketSend();\r
912                         }\r
913                 }\r
914                 else if(rval==SOCKET_ERROR)\r
915                 {\r
916                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());   \r
917                 }\r
918 \r
919 //      }while(!Disconnected() && !ZThread::Thread::interrupted());\r
920         }while(!Disconnected() && !IsCancelled());\r
921 \r
922         Disconnect();\r
923 \r
924 }\r
925 \r
926 void NNTPConnection::SendArticleOverInfo(Message &message)\r
927 {\r
928         std::string tempval;\r
929         std::string line;\r
930         std::map<long,std::string> references;\r
931 \r
932         StringFunctions::Convert(message.GetMessageID(),tempval);\r
933         line=tempval+"\t";\r
934         line+=message.GetSubject()+"\t";\r
935         line+=message.GetFromName()+"\t";\r
936         line+=message.GetDateTime().Format("%a, %d %b %y %H:%M:%S -0000")+"\t";\r
937         line+=message.GetNNTPArticleID()+"\t";\r
938         references=message.GetInReplyTo();\r
939         if(references.size()>0)\r
940         {\r
941                 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
942                 {\r
943                         if(i!=references.rbegin())\r
944                         {\r
945                                 line+=" ";\r
946                         }\r
947                         line+="<"+(*i).second+">";\r
948                 }\r
949                 line+="\t";\r
950         }\r
951         else\r
952         {\r
953                 line+="\t";\r
954         }\r
955         line+="\t";\r
956 \r
957         SendBufferedLine(line);\r
958 }\r
959 \r
960 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
961 {\r
962         bool sendheaders,sendbody;\r
963         std::string successcode;\r
964 \r
965         if(command.m_command=="ARTICLE")\r
966         {\r
967                 sendheaders=true;\r
968                 sendbody=true;\r
969                 successcode="220";\r
970         }\r
971         else if(command.m_command=="HEAD")\r
972         {\r
973                 sendheaders=true;\r
974                 sendbody=false;\r
975                 successcode="221";\r
976         }\r
977         else if(command.m_command=="BODY")\r
978         {\r
979                 sendheaders=false;\r
980                 sendbody=true;\r
981                 successcode="222";\r
982         }\r
983         else if(command.m_command=="STAT")\r
984         {\r
985                 sendheaders=false;\r
986                 sendbody=false;\r
987                 successcode="223";\r
988         }\r
989 \r
990         Message message;\r
991         int messageid=m_status.m_messageid;\r
992         std::string articleid="";\r
993         int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
994 \r
995         if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
996         {\r
997                 if(command.m_arguments[0].find("<")==std::string::npos)\r
998                 {\r
999                         StringFunctions::Convert(command.m_arguments[0],messageid);\r
1000                         message.Load(messageid,m_status.m_boardid);\r
1001                         m_status.m_messageid=message.GetMessageID();\r
1002                         type=1;\r
1003                 }\r
1004                 else\r
1005                 {\r
1006                         articleid=command.m_arguments[0];\r
1007                         message.Load(articleid);\r
1008                         type=2;\r
1009                 }\r
1010         }\r
1011         else\r
1012         {\r
1013                 message.Load(m_status.m_messageid,m_status.m_boardid);\r
1014         }\r
1015 \r
1016         switch(type)\r
1017         {\r
1018         case 0:\r
1019                 if(m_status.m_boardid!=-1)\r
1020                 {\r
1021                         if(m_status.m_messageid!=-1)\r
1022                         {\r
1023                                 std::ostringstream tempstr;\r
1024                                 std::string article;\r
1025                                 if(sendheaders&&sendbody)\r
1026                                 {\r
1027                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1028                                 }\r
1029                                 else if(sendheaders && !sendbody)\r
1030                                 {\r
1031                                         article=message.GetNNTPHeaders();\r
1032                                         // strip off final \r\n from headers\r
1033                                         if(article.rfind("\r\n")==article.size()-2)\r
1034                                         {\r
1035                                                 article.erase(article.size()-2);\r
1036                                         }\r
1037                                 }\r
1038                                 else\r
1039                                 {\r
1040                                         article=message.GetNNTPBody();\r
1041                                 }\r
1042                                 // dot stuff article\r
1043                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1044 \r
1045                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1046 \r
1047                                 SendBufferedLine(tempstr.str());\r
1048                                 if(sendheaders || sendbody)\r
1049                                 {\r
1050                                         SendBufferedLine(article);\r
1051                                         SendBufferedLine(".");\r
1052                                 }\r
1053 \r
1054                         }\r
1055                         else\r
1056                         {\r
1057                                 SendBufferedLine("420 Current article number is invalid");\r
1058                         }\r
1059                 }\r
1060                 else\r
1061                 {\r
1062                         SendBufferedLine("412 No newsgroup selected");\r
1063                 }\r
1064                 break;\r
1065         case 1:\r
1066                 if(m_status.m_boardid!=-1)\r
1067                 {\r
1068                         if(message.GetMessageID()!=-1)\r
1069                         {\r
1070                                 std::ostringstream tempstr;\r
1071                                 std::string article;\r
1072                                 if(sendheaders&&sendbody)\r
1073                                 {\r
1074                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1075                                 }\r
1076                                 else if(sendheaders && !sendbody)\r
1077                                 {\r
1078                                         article=message.GetNNTPHeaders();\r
1079                                         // strip off final \r\n from headers\r
1080                                         if(article.rfind("\r\n")==article.size()-2)\r
1081                                         {\r
1082                                                 article.erase(article.size()-2);\r
1083                                         }\r
1084                                 }\r
1085                                 else\r
1086                                 {\r
1087                                         article=message.GetNNTPBody();\r
1088                                 }\r
1089                                 // dot stuff article\r
1090                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1091 \r
1092                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1093 \r
1094                                 SendBufferedLine(tempstr.str());\r
1095                                 if(sendheaders || sendbody)\r
1096                                 {\r
1097                                         SendBufferedLine(article);\r
1098                                         SendBufferedLine(".");\r
1099                                 }\r
1100                         }\r
1101                         else\r
1102                         {\r
1103                                 SendBufferedLine("423 No article with that number");\r
1104                         }\r
1105                 }\r
1106                 else\r
1107                 {\r
1108                         SendBufferedLine("412 No newsgroup selected");\r
1109                 }\r
1110                 break;\r
1111         case 2:\r
1112                 if(message.GetMessageID()!=-1)\r
1113                 {\r
1114                         std::string article;\r
1115                         if(sendheaders&&sendbody)\r
1116                         {\r
1117                                 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1118                         }\r
1119                         else if(sendheaders && !sendbody)\r
1120                         {\r
1121                                 article=message.GetNNTPHeaders();\r
1122                                 // strip off final \r\n from headers\r
1123                                 if(article.rfind("\r\n")==article.size()-2)\r
1124                                 {\r
1125                                         article.erase(article.size()-2);\r
1126                                 }\r
1127                         }\r
1128                         else\r
1129                         {\r
1130                                 article=message.GetNNTPBody();\r
1131                         }\r
1132                         // dot stuff article\r
1133                         article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1134 \r
1135                         SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
1136                         if(sendheaders || sendbody)\r
1137                         {\r
1138                                 SendBufferedLine(article);\r
1139                                 SendBufferedLine(".");\r
1140                         }\r
1141                 }\r
1142                 else\r
1143                 {\r
1144                         SendBufferedLine("430 No article with that message-id");\r
1145                 }\r
1146                 break;\r
1147         }\r
1148 \r
1149 }\r
1150 \r
1151 void NNTPConnection::SendBuffered(const std::string &data)\r
1152 {\r
1153         m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
1154 }\r
1155 \r
1156 void NNTPConnection::SocketReceive()\r
1157 {\r
1158         int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
1159         if(rval>0)\r
1160         {\r
1161                 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
1162         }\r
1163         else if(rval==0)\r
1164         {\r
1165                 Disconnect();\r
1166                 m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::SocketReceive remote host closed connection");\r
1167         }\r
1168         else if(rval==-1)\r
1169         {\r
1170                 std::string errnostr;\r
1171                 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1172                 // error on receive - close the connection\r
1173                 Disconnect();\r
1174                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1175         }\r
1176 }\r
1177 \r
1178 void NNTPConnection::SocketSend()\r
1179 {\r
1180         if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
1181         {\r
1182                 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
1183                 if(rval>0)\r
1184                 {       \r
1185                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
1186                 }\r
1187                 else if(rval==-1)\r
1188                 {\r
1189                         std::string errnostr;\r
1190                         StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1191                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1192                 }\r
1193         }\r
1194 }\r