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