fa35f4f831e8f0d5cb8f84a9b6cf41a0cbacbab6
[fms.git] / src / nntp / nntpconnection.cpp
1 #include "../../include/nntp/nntpconnection.h"\r
2 #include "../../include/nntp/uwildmat.h"\r
3 #include "../../include/stringfunctions.h"\r
4 #include "../../include/datetime.h"\r
5 #include "../../include/boardlist.h"\r
6 #include "../../include/message.h"\r
7 #include "../../include/messagelist.h"\r
8 #include "../../include/option.h"\r
9 \r
10 #include <algorithm>\r
11 \r
12 //#include <zthread/Thread.h>\r
13 #include "../../include/pthreadwrapper/thread.h"\r
14 \r
15 #ifdef XMEM\r
16         #include <xmem.h>\r
17 #endif\r
18 \r
19 NNTPConnection::NNTPConnection(SOCKET sock)\r
20 {\r
21         std::string tempval;\r
22 \r
23         m_socket=sock;\r
24         m_tempbuffer.resize(32768);\r
25         \r
26         m_status.m_isposting=false;\r
27         m_status.m_allowpost=false;\r
28         m_status.m_boardid=-1;\r
29         m_status.m_messageid=-1;\r
30         m_status.m_mode=MODE_NONE;\r
31 \r
32         Option::Instance()->Get("NNTPAllowPost",tempval);\r
33         if(tempval=="true")\r
34         {\r
35                 m_status.m_allowpost=true;\r
36         }\r
37 \r
38 }\r
39 \r
40 NNTPConnection::~NNTPConnection()\r
41 {\r
42 \r
43 }\r
44 \r
45 void NNTPConnection::Disconnect()\r
46 {\r
47         if(m_socket!=INVALID_SOCKET)\r
48         {\r
49         #ifdef _WIN32\r
50                 closesocket(m_socket);\r
51         #else\r
52                 close(m_socket);\r
53         #endif\r
54                 m_socket=INVALID_SOCKET;\r
55         }\r
56 }\r
57 \r
58 std::vector<char>::iterator NNTPConnection::Find(std::vector<char> &buffer, const std::string &val)\r
59 {\r
60         return std::search(buffer.begin(),buffer.end(),val.begin(),val.end());\r
61 }\r
62 \r
63 const bool NNTPConnection::HandleArticleCommand(const NNTPCommand &command)\r
64 {\r
65 \r
66         SendArticleParts(command);\r
67         \r
68         return true;\r
69 }\r
70 \r
71 const bool NNTPConnection::HandleBodyCommand(const NNTPCommand &command)\r
72 {\r
73         SendArticleParts(command);\r
74 \r
75         return true;\r
76 }\r
77 \r
78 const bool NNTPConnection::HandleCapabilitiesCommand(const NNTPCommand &command)\r
79 {\r
80         \r
81         SendBufferedLine("101 Capability list :");\r
82         SendBufferedLine("VERSION 2");\r
83         SendBufferedLine("MODE-READER");\r
84         SendBufferedLine("READER");\r
85         SendBufferedLine("LIST OVERVIEW.FMT");\r
86         SendBufferedLine("OVER MSGID");\r
87         if(m_status.m_allowpost==true)\r
88         {\r
89                 SendBufferedLine("POST");\r
90         }\r
91         SendBufferedLine(".");\r
92         \r
93         return true;\r
94 }\r
95 \r
96 const bool NNTPConnection::HandleCommand(const NNTPCommand &command)\r
97 {\r
98         if(command.m_command=="QUIT")\r
99         {\r
100                 return HandleQuitCommand(command);\r
101         }\r
102         if(command.m_command=="MODE")\r
103         {\r
104                 return HandleModeCommand(command);\r
105         }\r
106         if(command.m_command=="CAPABILITIES")\r
107         {\r
108                 return HandleCapabilitiesCommand(command);\r
109         }\r
110         if(command.m_command=="HELP")\r
111         {\r
112                 return HandleHelpCommand(command);\r
113         }\r
114         if(command.m_command=="DATE")\r
115         {\r
116                 return HandleDateCommand(command);\r
117         }\r
118         if(command.m_command=="LIST")\r
119         {\r
120                 return HandleListCommand(command);\r
121         }\r
122         if(command.m_command=="GROUP")\r
123         {\r
124                 return HandleGroupCommand(command);\r
125         }\r
126         if(command.m_command=="LISTGROUP")\r
127         {\r
128                 return HandleListGroupCommand(command);\r
129         }\r
130         if(command.m_command=="LAST")\r
131         {\r
132                 return HandleLastCommand(command);\r
133         }\r
134         if(command.m_command=="NEXT")\r
135         {\r
136                 return HandleNextCommand(command);\r
137         }\r
138         if(command.m_command=="ARTICLE")\r
139         {\r
140                 return HandleArticleCommand(command);\r
141         }\r
142         if(command.m_command=="HEAD")\r
143         {\r
144                 return HandleHeadCommand(command);\r
145         }\r
146         if(command.m_command=="BODY")\r
147         {\r
148                 return HandleBodyCommand(command);\r
149         }\r
150         if(command.m_command=="STAT")\r
151         {\r
152                 return HandleStatCommand(command);\r
153         }\r
154         if(command.m_command=="NEWGROUPS")\r
155         {\r
156                 return HandleNewGroupsCommand(command);\r
157         }\r
158         if(command.m_command=="POST")\r
159         {\r
160                 return HandlePostCommand(command);\r
161         }\r
162         if(command.m_command=="OVER" || command.m_command=="XOVER")\r
163         {\r
164                 return HandleOverCommand(command);\r
165         }\r
166 \r
167         return false;\r
168 }\r
169 \r
170 const bool NNTPConnection::HandleDateCommand(const NNTPCommand &command)\r
171 {\r
172         DateTime now;\r
173         now.SetToGMTime();\r
174         SendBufferedLine("111 "+now.Format("%Y%m%d%H%M%S"));\r
175         return true;\r
176 }\r
177 \r
178 const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)\r
179 {\r
180         if(command.m_arguments.size()==1)\r
181         {\r
182                 Board board;\r
183                 if(board.Load(command.m_arguments[0])==true)\r
184                 {\r
185                         std::ostringstream tempstr;\r
186 \r
187                         tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
188 \r
189                         SendBufferedLine(tempstr.str());\r
190 \r
191                         // set the current boardid to this one\r
192                         m_status.m_boardid=board.GetBoardID();\r
193                         //set the first message id, -1 if there are no messages\r
194                         board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
195 \r
196                 }\r
197                 else\r
198                 {\r
199                         SendBufferedLine("411 No such newsgroup");\r
200                 }\r
201         }\r
202         else\r
203         {\r
204                 SendBufferedLine("501 Syntax error");\r
205                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleGroupCommand syntax error");\r
206         }\r
207 \r
208         return true;\r
209 }\r
210 \r
211 const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)\r
212 {\r
213         \r
214         SendArticleParts(command);\r
215 \r
216         return true;\r
217 }\r
218 \r
219 const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)\r
220 {\r
221         SendBufferedLine("100 Help text follows");\r
222         SendBufferedLine("There is no help text");\r
223         SendBufferedLine(".");\r
224 \r
225         return true;\r
226 }\r
227 \r
228 const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)\r
229 {\r
230         if(m_status.m_boardid!=-1)\r
231         {\r
232                 if(m_status.m_messageid!=-1)\r
233                 {\r
234                         Message mess;\r
235 \r
236                         if(mess.LoadPrevious(m_status.m_messageid,m_status.m_boardid))\r
237                         {\r
238                                 std::ostringstream tempstr;\r
239 \r
240                                 m_status.m_messageid=mess.GetMessageID();\r
241 \r
242                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
243 \r
244                                 SendBufferedLine(tempstr.str());\r
245 \r
246                         }\r
247                         else\r
248                         {\r
249                                 SendBufferedLine("422 No previous article in this group");\r
250                         }\r
251                 }\r
252                 else\r
253                 {\r
254                         SendBufferedLine("420 Current article number is invalid");\r
255                 }\r
256         }\r
257         else\r
258         {\r
259                 SendBufferedLine("412 No newsgroup selected");\r
260         }\r
261 \r
262         return true;\r
263 }\r
264 \r
265 const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)\r
266 {\r
267 \r
268         int type=1;     // default LIST type is active\r
269         std::string arg1="";\r
270         std::string arg2="";\r
271 \r
272         // type of LIST\r
273         if(command.m_arguments.size()>0)\r
274         {\r
275                 StringFunctions::UpperCase(command.m_arguments[0],arg1);\r
276                 if(arg1=="ACTIVE")\r
277                 {\r
278                         type=1;\r
279                 }\r
280                 else if(arg1=="NEWSGROUPS")\r
281                 {\r
282                         type=2;\r
283                 }\r
284                 else if(arg1=="OVERVIEW.FMT")\r
285                 {\r
286                         type=3;\r
287                 }\r
288                 else\r
289                 {\r
290                         type=0;\r
291                 }\r
292         }\r
293         // wildmat\r
294         if(command.m_arguments.size()>1)\r
295         {\r
296                 arg2=command.m_arguments[1];\r
297         }\r
298 \r
299         // LIST ACTIVE [wildmat]\r
300         if(type==1)\r
301         {\r
302                 bool show;\r
303                 std::ostringstream tempstr;\r
304                 BoardList bl;\r
305                 bl.Load();\r
306 \r
307                 SendBufferedLine("215 list of newsgroups follows");\r
308 \r
309                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
310                 {\r
311                         show=true;\r
312                         tempstr.str("");\r
313 \r
314                         // check wilmat match\r
315                         if(arg2!="")\r
316                         {\r
317                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
318                         }\r
319 \r
320                         if(show==true)\r
321                         {\r
322                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");\r
323                                 SendBufferedLine(tempstr.str());\r
324                         }\r
325                 }\r
326 \r
327                 SendBufferedLine(".");\r
328 \r
329         }\r
330         // LIST NEWSGROUPS\r
331         else if(type==2)\r
332         {\r
333                 bool show;\r
334                 std::ostringstream tempstr;\r
335                 BoardList bl;\r
336                 bl.Load();\r
337 \r
338                 SendBufferedLine("215 list of newsgroups follows");\r
339 \r
340                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
341                 {\r
342                         show=true;\r
343                         tempstr.str("");\r
344 \r
345                         // check wilmat match\r
346                         if(arg2!="")\r
347                         {\r
348                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
349                         }\r
350 \r
351                         if(show==true)\r
352                         {\r
353                                 tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();\r
354                                 SendBufferedLine(tempstr.str());\r
355                         }\r
356                 }\r
357 \r
358                 SendBufferedLine(".");\r
359 \r
360         }\r
361         // LIST OVERVIEW.FMT\r
362         else if(type==3)\r
363         {\r
364                 SendBufferedLine("215 Order of fields in overview database.");\r
365                 SendBufferedLine("Subject:");\r
366                 SendBufferedLine("From:");\r
367                 SendBufferedLine("Date:");\r
368                 SendBufferedLine("Message-ID:");\r
369                 SendBufferedLine("References:");\r
370                 SendBufferedLine(":bytes");\r
371                 SendBufferedLine(":lines");\r
372                 SendBufferedLine(".");\r
373         }\r
374         else\r
375         {\r
376                 // unknown arg\r
377                 SendBufferedLine("501 Syntax error");\r
378                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListCommand unhandled LIST variant");\r
379         }\r
380 \r
381         return true;\r
382 }\r
383 \r
384 const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)\r
385 {\r
386 \r
387         std::ostringstream tempstr;\r
388         Board board;\r
389         bool validgroup=false;\r
390         int lownum=-1;\r
391         int highnum=-1;\r
392 \r
393         // no args and invalid boardid\r
394         if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
395         {\r
396                 SendBufferedLine("412 No newsgroup selected");\r
397         }\r
398         else if(command.m_arguments.size()==0)\r
399         {\r
400                 validgroup=board.Load(m_status.m_boardid);\r
401         }\r
402         else if(command.m_arguments.size()==1)\r
403         {\r
404                 validgroup=board.Load(command.m_arguments[0]);\r
405                 if(validgroup)\r
406                 {\r
407                         lownum=board.GetLowMessageID();\r
408                         highnum=board.GetHighMessageID();\r
409                 }\r
410                 else\r
411                 {\r
412                         SendBufferedLine("411 No such newsgroup");\r
413                 }\r
414         }\r
415         else if(command.m_arguments.size()==2)\r
416         {\r
417                 validgroup=board.Load(command.m_arguments[0]);\r
418                 std::vector<std::string> rangeparts;\r
419                 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
420 \r
421                 if(rangeparts.size()>0)\r
422                 {\r
423                         StringFunctions::Convert(rangeparts[0],lownum);\r
424                 }\r
425                 if(rangeparts.size()>1)\r
426                 {\r
427                         StringFunctions::Convert(rangeparts[1],highnum);\r
428                 }\r
429 \r
430         }\r
431         else\r
432         {\r
433                 // unknown arg\r
434                 SendBufferedLine("501 Syntax error");\r
435                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListGroupCommand unknown arguments");\r
436         }\r
437 \r
438         if(validgroup)\r
439         {\r
440 \r
441                 // set boardid and messageid\r
442                 m_status.m_boardid=board.GetBoardID();\r
443                 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
444 \r
445                 if(lownum==-1)\r
446                 {\r
447                         lownum=board.GetLowMessageID();\r
448                 }\r
449                 if(highnum==-1)\r
450                 {\r
451                         highnum=board.GetHighMessageID();\r
452                 }\r
453 \r
454                 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
455                 SendBufferedLine(tempstr.str());\r
456 \r
457                 MessageList ml;\r
458                 ml.LoadRange(lownum,highnum,board.GetBoardID());\r
459 \r
460                 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
461                 {\r
462                         tempstr.str("");\r
463                         tempstr << (*i).GetMessageID();\r
464 \r
465                         SendBufferedLine(tempstr.str());\r
466                 }\r
467 \r
468                 // end of multi-line response\r
469                 SendBufferedLine(".");\r
470 \r
471         }\r
472 \r
473         return true;\r
474 }\r
475 \r
476 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
477 {\r
478         if(command.m_arguments.size()>0)\r
479         {\r
480                 std::string arg=command.m_arguments[0];\r
481                 StringFunctions::UpperCase(arg,arg);\r
482                 if(arg=="READER")\r
483                 {\r
484                         m_status.m_mode=MODE_READER;\r
485                         if(m_status.m_allowpost==true)\r
486                         {\r
487                                 SendBufferedLine("200 Posting allowed");\r
488                         }\r
489                         else\r
490                         {\r
491                                 SendBufferedLine("201 Posting prohibited");\r
492                         }\r
493                         \r
494                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand set mode to reader");\r
495                 }\r
496                 else\r
497                 {\r
498                         SendBufferedLine("501 Syntax error");\r
499                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
500                 }\r
501         }\r
502         else\r
503         {\r
504                 SendBufferedLine("501 Syntax error");\r
505                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand no argument supplied for MODE");     \r
506         }\r
507 \r
508         return true;\r
509 }\r
510 \r
511 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
512 {\r
513         if(command.m_arguments.size()>=2)\r
514         {\r
515                 DateTime date;\r
516                 int tempint;\r
517                 if(command.m_arguments[0].size()==8)\r
518                 {\r
519                         StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempint);\r
520                         date.SetYear(tempint);\r
521                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
522                         date.SetMonth(tempint);\r
523                         StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempint);\r
524                         date.SetDay(tempint);\r
525                 }\r
526                 else\r
527                 {\r
528                         /*\r
529                         from RFC 3977\r
530                         If the first two digits of the year are not specified\r
531                         (this is supported only for backward compatibility), the year is to\r
532                         be taken from the current century if yy is smaller than or equal to\r
533                         the current year, and the previous century otherwise.\r
534                         */\r
535                         int century;\r
536                         DateTime now;\r
537                         now.SetToGMTime();\r
538                         century=now.GetYear()-(now.GetYear()%100);\r
539 \r
540                         StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempint);\r
541                         tempint<=now.GetYear()-century ? tempint+=century : tempint+=(century-100);\r
542                         \r
543                         //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
544                         \r
545                         date.SetYear(tempint);\r
546                         StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempint);\r
547                         date.SetMonth(tempint);\r
548                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
549                         date.SetDay(tempint);\r
550                 }\r
551 \r
552                 date.Normalize();\r
553 \r
554                 BoardList bl;\r
555 \r
556                 bl.LoadNew(date.Format("%Y-%m-%d %H:%M:%S"));\r
557 \r
558                 SendBufferedLine("231 List of new newsgroups follows");\r
559 \r
560                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
561                 {\r
562                         std::ostringstream tempstr;\r
563                         tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
564                         SendBufferedLine(tempstr.str());\r
565                 }\r
566 \r
567                 SendBufferedLine(".");\r
568 \r
569         }\r
570         else\r
571         {\r
572                 SendBufferedLine("501 Syntax error");\r
573                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleNewGroupsCommand syntax error");\r
574         }\r
575 \r
576         return true;\r
577 \r
578 }\r
579 \r
580 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
581 {\r
582         if(m_status.m_boardid!=-1)\r
583         {\r
584                 if(m_status.m_messageid!=-1)\r
585                 {\r
586                         Message mess;\r
587 \r
588                         if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))\r
589                         {\r
590                                 std::ostringstream tempstr;\r
591 \r
592                                 m_status.m_messageid=mess.GetMessageID();\r
593 \r
594                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
595 \r
596                                 SendBufferedLine(tempstr.str());\r
597 \r
598                         }\r
599                         else\r
600                         {\r
601                                 SendBufferedLine("421 No next article in this group");\r
602                         }\r
603                 }\r
604                 else\r
605                 {\r
606                         SendBufferedLine("420 Current article number is invalid");\r
607                 }\r
608         }\r
609         else\r
610         {\r
611                 SendBufferedLine("412 No newsgroup selected");\r
612         }\r
613 \r
614         return true;\r
615 \r
616 }\r
617 \r
618 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
619 {\r
620         long lowmessageid,highmessageid;\r
621         std::string messageuuid="";\r
622 \r
623         lowmessageid=highmessageid=-2;\r
624 \r
625         if(command.m_arguments.size()==0)\r
626         {\r
627                 lowmessageid=m_status.m_messageid;\r
628                 highmessageid=m_status.m_messageid;\r
629         }\r
630         else\r
631         {\r
632                 // Message-ID\r
633                 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
634                 {\r
635                         messageuuid=command.m_arguments[0];\r
636                         messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
637                         messageuuid=StringFunctions::Replace(messageuuid,">","");\r
638                         /*\r
639                         // get rid of @ and everything after\r
640                         if(messageuuid.find("@")!=std::string::npos)\r
641                         {\r
642                                 messageuuid.erase(messageuuid.find("@"));\r
643                         }\r
644                         */\r
645                 }\r
646                 // single article or range\r
647                 else\r
648                 {\r
649                         // range\r
650                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
651                         {\r
652                                 std::vector<std::string> rangeparts;\r
653                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
654                                 // no upper bound\r
655                                 if(rangeparts.size()>0)\r
656                                 {\r
657                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
658                                         highmessageid=-1;\r
659                                 }\r
660                                 //upper bound\r
661                                 else if(rangeparts.size()>1)\r
662                                 {\r
663                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
664                                 }\r
665                         }\r
666                         // single\r
667                         else\r
668                         {\r
669                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
670                         }\r
671                 }\r
672         }\r
673 \r
674         if(messageuuid!="")\r
675         {\r
676                 Message mess;\r
677                 if(mess.Load(messageuuid))\r
678                 {\r
679                         SendBufferedLine("224 Overview information follows");\r
680                         SendArticleOverInfo(mess);\r
681                         SendBufferedLine(".");\r
682                 }\r
683                 else\r
684                 {\r
685                         SendBufferedLine("423 No such article");\r
686                 }\r
687         }\r
688         else\r
689         {\r
690                 Board bd;\r
691                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
692                 {\r
693                         // single message\r
694                         if(highmessageid==-2)\r
695                         {\r
696                                 Message mess;\r
697                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
698                                 {\r
699                                         SendBufferedLine("224 Overview information follows");\r
700                                         SendArticleOverInfo(mess);\r
701                                         SendBufferedLine(".");\r
702                                 }\r
703                                 else\r
704                                 {\r
705                                         SendBufferedLine("423 No such article in this group");\r
706                                 }\r
707                         }\r
708                         // range with no upper bound\r
709                         else if(highmessageid==-1)\r
710                         {\r
711                                 MessageList ml;\r
712                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
713                                 if(ml.size()>0)\r
714                                 {\r
715                                         SendBufferedLine("224 Overview information follows");\r
716                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
717                                         {\r
718                                                 SendArticleOverInfo((*i));\r
719                                         }\r
720                                         SendBufferedLine(".");\r
721                                 }\r
722                                 else\r
723                                 {\r
724                                         SendBufferedLine("423 Empty range");\r
725                                 }\r
726                         }\r
727                         // range with upper and lower bound\r
728                         else if(highmessageid>=lowmessageid)\r
729                         {\r
730                                 MessageList ml;\r
731                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
732                                 if(ml.size()>0)\r
733                                 {\r
734                                         SendBufferedLine("224 Overview information follows");\r
735                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
736                                         {\r
737                                                 SendArticleOverInfo((*i));\r
738                                         }\r
739                                         SendBufferedLine(".");\r
740                                 }\r
741                                 else\r
742                                 {\r
743                                         SendBufferedLine("423 Empty range");\r
744                                 }\r
745                         }\r
746                         // invalid range\r
747                         else\r
748                         {\r
749                                 SendBufferedLine("423 Empty range");\r
750                         }\r
751                 }\r
752                 else\r
753                 {\r
754                         SendBufferedLine("423 No newsgroup selected");\r
755                 }\r
756         }\r
757 \r
758         return true;\r
759 \r
760 }\r
761 \r
762 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
763 {\r
764         if(m_status.m_allowpost==true)\r
765         {\r
766                 SendBufferedLine("340 Send article to be posted");\r
767                 m_status.m_isposting=true;\r
768         }\r
769         else\r
770         {\r
771                 SendBufferedLine("440 Posting not permitted");\r
772         }\r
773 \r
774         return true;\r
775 }\r
776 \r
777 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
778 {\r
779         Message mess;\r
780 \r
781         if(mess.ParseNNTPMessage(message))\r
782         {\r
783                 if(mess.PostedToAdministrationBoard()==true)\r
784                 {\r
785                         mess.HandleAdministrationMessage();\r
786                 }\r
787                 else\r
788                 {\r
789                         if(mess.StartFreenetInsert())\r
790                         {\r
791                                 SendBufferedLine("240 Article received OK");\r
792                         }\r
793                         else\r
794                         {\r
795                                 SendBufferedLine("441 Posting failed.  Make sure the identity you are sending with exists!");\r
796                         }\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