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