0212e239b8149c87a786193301dc45c847229299
[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/boardlist.h"\r
5 #include "../../include/message.h"\r
6 #include "../../include/messagelist.h"\r
7 #include "../../include/option.h"\r
8 #include "../../include/nntp/extensiontrust.h"\r
9 #include "../../include/threadwrapper/cancelablethread.h"\r
10 \r
11 #include <algorithm>\r
12 #include <Poco/DateTime.h>\r
13 #include <Poco/DateTimeFormatter.h>\r
14 #include <Poco/Timestamp.h>\r
15 \r
16 #ifdef XMEM\r
17         #include <xmem.h>\r
18 #endif\r
19 \r
20 NNTPConnection::NNTPConnection(SOCKET sock):m_socket(sock)\r
21 {\r
22         std::string tempval("");\r
23 \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         m_status.m_authenticated=false;\r
32 \r
33         Option::Instance()->Get("NNTPAllowPost",tempval);\r
34         if(tempval=="true")\r
35         {\r
36                 m_status.m_allowpost=true;\r
37         }\r
38 \r
39 }\r
40 \r
41 NNTPConnection::~NNTPConnection()\r
42 {\r
43 \r
44 }\r
45 \r
46 void NNTPConnection::Disconnect()\r
47 {\r
48         if(m_socket!=INVALID_SOCKET)\r
49         {\r
50         #ifdef _WIN32\r
51                 closesocket(m_socket);\r
52         #else\r
53                 close(m_socket);\r
54         #endif\r
55                 m_socket=INVALID_SOCKET;\r
56         }\r
57 }\r
58 \r
59 std::vector<char>::iterator NNTPConnection::Find(std::vector<char> &buffer, const std::string &val)\r
60 {\r
61         return std::search(buffer.begin(),buffer.end(),val.begin(),val.end());\r
62 }\r
63 \r
64 const bool NNTPConnection::HandleArticleCommand(const NNTPCommand &command)\r
65 {\r
66 \r
67         SendArticleParts(command);\r
68         \r
69         return true;\r
70 }\r
71 \r
72 const bool NNTPConnection::HandleAuthInfoCommand(const NNTPCommand &command)\r
73 {\r
74         if(command.m_arguments.size()<2)\r
75         {\r
76                 SendBufferedLine("501 Syntax error");\r
77         }\r
78         else if(m_status.m_authenticated==true)\r
79         {\r
80                 SendBufferedLine("502 Command unavailable");            // not available when already authenticated\r
81         }\r
82         else\r
83         {\r
84                 std::string arg=command.m_arguments[0];\r
85                 StringFunctions::UpperCase(arg,arg);\r
86                 std::string name="";\r
87                 // get remaining args as part of the name since a name might have a space and the args are split on spaces\r
88                 for(std::vector<std::string>::const_iterator i=command.m_arguments.begin()+1; i!=command.m_arguments.end(); i++)\r
89                 {\r
90                         // we split on the space, so add it back\r
91                         if(i!=command.m_arguments.begin()+1)\r
92                         {\r
93                                 name+=" ";\r
94                         }       \r
95                         name+=(*i);\r
96                 }\r
97                 if(arg=="USER")\r
98                 {\r
99                         LocalIdentity localid;\r
100                         if(localid.Load(name))\r
101                         {\r
102                                 m_status.m_authuser=localid;\r
103                                 m_status.m_authenticated=true;\r
104                                 SendBufferedLine("281 Authentication accepted");\r
105                         }\r
106                         else\r
107                         {\r
108                                 SendBufferedLine("481 Authentication failed");\r
109                         }\r
110                 }\r
111                 else if(arg=="PASS")\r
112                 {\r
113                         SendBufferedLine("482 Authentication commands issued out of sequence"); // only require username\r
114                 }\r
115                 else\r
116                 {\r
117                         SendBufferedLine("501 Syntax error");\r
118                 }\r
119         }\r
120 \r
121         return true;\r
122 }\r
123 \r
124 const bool NNTPConnection::HandleBodyCommand(const NNTPCommand &command)\r
125 {\r
126         SendArticleParts(command);\r
127 \r
128         return true;\r
129 }\r
130 \r
131 const bool NNTPConnection::HandleCapabilitiesCommand(const NNTPCommand &command)\r
132 {\r
133         \r
134         SendBufferedLine("101 Capability list :");\r
135         SendBufferedLine("VERSION 2");\r
136         if(m_status.m_authenticated==false)             // RFC 4643 2.2 0 - don't advertise MODE-READER after authentication\r
137         {\r
138                 SendBufferedLine("MODE-READER");\r
139         }\r
140         SendBufferedLine("READER");\r
141         SendBufferedLine("LIST OVERVIEW.FMT");\r
142         SendBufferedLine("OVER MSGID");\r
143         if(m_status.m_allowpost==true)\r
144         {\r
145                 SendBufferedLine("POST");\r
146         }\r
147         if(m_status.m_authenticated==false)\r
148         {\r
149                 SendBufferedLine("AUTHINFO USER");\r
150         }\r
151         SendBufferedLine("XFMSTRUST");\r
152         SendBufferedLine(".");\r
153         \r
154         return true;\r
155 }\r
156 \r
157 const bool NNTPConnection::HandleCommand(const NNTPCommand &command)\r
158 {\r
159         if(command.m_command=="QUIT")\r
160         {\r
161                 return HandleQuitCommand(command);\r
162         }\r
163         if(command.m_command=="MODE")\r
164         {\r
165                 return HandleModeCommand(command);\r
166         }\r
167         if(command.m_command=="CAPABILITIES")\r
168         {\r
169                 return HandleCapabilitiesCommand(command);\r
170         }\r
171         if(command.m_command=="HELP")\r
172         {\r
173                 return HandleHelpCommand(command);\r
174         }\r
175         if(command.m_command=="DATE")\r
176         {\r
177                 return HandleDateCommand(command);\r
178         }\r
179         if(command.m_command=="LIST")\r
180         {\r
181                 return HandleListCommand(command);\r
182         }\r
183         if(command.m_command=="GROUP")\r
184         {\r
185                 return HandleGroupCommand(command);\r
186         }\r
187         if(command.m_command=="LISTGROUP")\r
188         {\r
189                 return HandleListGroupCommand(command);\r
190         }\r
191         if(command.m_command=="LAST")\r
192         {\r
193                 return HandleLastCommand(command);\r
194         }\r
195         if(command.m_command=="NEXT")\r
196         {\r
197                 return HandleNextCommand(command);\r
198         }\r
199         if(command.m_command=="ARTICLE")\r
200         {\r
201                 return HandleArticleCommand(command);\r
202         }\r
203         if(command.m_command=="HEAD")\r
204         {\r
205                 return HandleHeadCommand(command);\r
206         }\r
207         if(command.m_command=="BODY")\r
208         {\r
209                 return HandleBodyCommand(command);\r
210         }\r
211         if(command.m_command=="STAT")\r
212         {\r
213                 return HandleStatCommand(command);\r
214         }\r
215         if(command.m_command=="NEWGROUPS")\r
216         {\r
217                 return HandleNewGroupsCommand(command);\r
218         }\r
219         if(command.m_command=="POST")\r
220         {\r
221                 return HandlePostCommand(command);\r
222         }\r
223         if(command.m_command=="OVER" || command.m_command=="XOVER")\r
224         {\r
225                 return HandleOverCommand(command);\r
226         }\r
227         if(command.m_command=="AUTHINFO")\r
228         {\r
229                 return HandleAuthInfoCommand(command);\r
230         }\r
231         if(command.m_command=="XGETTRUST")\r
232         {\r
233                 return HandleGetTrustCommand(command);\r
234         }\r
235         if(command.m_command=="XSETTRUST")\r
236         {\r
237                 return HandleSetTrustCommand(command);\r
238         }\r
239         if(command.m_command=="XGETTRUSTLIST")\r
240         {\r
241                 return HandleGetTrustListCommand(command);\r
242         }\r
243 \r
244         return false;\r
245 }\r
246 \r
247 const bool NNTPConnection::HandleDateCommand(const NNTPCommand &command)\r
248 {\r
249         Poco::DateTime now;\r
250         SendBufferedLine("111 "+Poco::DateTimeFormatter::format(now,"%Y%m%d%H%M%S"));\r
251         return true;\r
252 }\r
253 \r
254 const bool NNTPConnection::HandleGetTrustCommand(const NNTPCommand &command)\r
255 {\r
256         if(command.m_arguments.size()>=2)\r
257         {\r
258                 std::string type=command.m_arguments[0];\r
259                 StringFunctions::UpperCase(type,type);\r
260                 if(type=="MESSAGE" || type=="TRUSTLIST" || type=="PEERMESSAGE" || type=="PEERTRUSTLIST")\r
261                 {\r
262                         if(m_status.m_authenticated)\r
263                         {\r
264                                 bool found=false;\r
265                                 int trust=-1;\r
266                                 std::string nntpname="";\r
267                                 for(int i=1; i<command.m_arguments.size(); i++)\r
268                                 {\r
269                                         if(i!=1)\r
270                                         {\r
271                                                 nntpname+=" ";\r
272                                         }\r
273                                         nntpname+=command.m_arguments[i];\r
274                                 }\r
275 \r
276                                 TrustExtension tr(m_status.m_authuser.GetID());\r
277 \r
278                                 if(type=="MESSAGE")\r
279                                 {\r
280                                         if(tr.GetMessageTrust(nntpname,trust))\r
281                                         {\r
282                                                 found=true;\r
283                                         }\r
284                                 }\r
285                                 else if(type=="TRUSTLIST")\r
286                                 {\r
287                                         if(tr.GetTrustListTrust(nntpname,trust))\r
288                                         {\r
289                                                 found=true;\r
290                                         }\r
291                                 }\r
292                                 else if(type=="PEERMESSAGE")\r
293                                 {\r
294                                         if(tr.GetPeerMessageTrust(nntpname,trust))\r
295                                         {\r
296                                                 found=true;\r
297                                         }\r
298                                 }\r
299                                 else if(type=="PEERTRUSTLIST")\r
300                                 {\r
301                                         if(tr.GetPeerTrustListTrust(nntpname,trust))\r
302                                         {\r
303                                                 found=true;\r
304                                         }\r
305                                 }\r
306 \r
307                                 if(trust>=0 && found)\r
308                                 {\r
309                                         std::string truststr="";\r
310                                         StringFunctions::Convert(trust,truststr);\r
311                                         SendBufferedLine("280 "+truststr);\r
312                                 }\r
313                                 else if(found)\r
314                                 {\r
315                                         SendBufferedLine("281 null");\r
316                                 }\r
317                                 else\r
318                                 {\r
319                                         SendBufferedLine("480 Identity not found");\r
320                                 }\r
321 \r
322                         }\r
323                         else\r
324                         {\r
325                                 SendBufferedLine("480 User not authenticated");\r
326                         }\r
327                 }\r
328                 else\r
329                 {\r
330                         SendBufferedLine("501 Syntax error");\r
331                 }\r
332         }\r
333         else\r
334         {\r
335                 SendBufferedLine("501 Syntax error");\r
336         }\r
337         return true;\r
338 }       \r
339 \r
340 const bool NNTPConnection::HandleGetTrustListCommand(const NNTPCommand &command)\r
341 {\r
342         if(m_status.m_authenticated)\r
343         {\r
344                 TrustExtension tr(m_status.m_authuser.GetID());\r
345                 std::map<std::string,TrustExtension::trust> trustlist;\r
346                 if(tr.GetTrustList(trustlist))\r
347                 {\r
348                         SendBufferedLine("280 Trust list follows");\r
349                         for(std::map<std::string,TrustExtension::trust>::iterator i=trustlist.begin(); i!=trustlist.end(); i++)\r
350                         {\r
351                                 std::ostringstream tempstr;\r
352                                 tempstr << (*i).first << "\t";\r
353                                 if((*i).second.m_localmessagetrust>-1)\r
354                                 {\r
355                                         tempstr << (*i).second.m_localmessagetrust;\r
356                                 } \r
357                                 else\r
358                                 {\r
359                                         tempstr << "null";\r
360                                 }\r
361                                 tempstr << "\t";\r
362                                 if((*i).second.m_localtrustlisttrust>-1)\r
363                                 {\r
364                                         tempstr << (*i).second.m_localtrustlisttrust;\r
365                                 }\r
366                                 else\r
367                                 {\r
368                                         tempstr << "null";\r
369                                 }\r
370                                 tempstr << "\t";\r
371                                 if((*i).second.m_peermessagetrust>-1)\r
372                                 {\r
373                                         tempstr << (*i).second.m_peermessagetrust;\r
374                                 }\r
375                                 else\r
376                                 {\r
377                                         tempstr << "null";\r
378                                 }\r
379                                 tempstr << "\t";\r
380                                 if((*i).second.m_peertrustlisttrust>-1)\r
381                                 {\r
382                                         tempstr << (*i).second.m_peertrustlisttrust;\r
383                                 }\r
384                                 else\r
385                                 {\r
386                                         tempstr << "null";\r
387                                 }\r
388                                 tempstr << "\t";\r
389                                 tempstr << (*i).second.m_messagetrustcomment;\r
390                                 tempstr << "\t";\r
391                                 tempstr << (*i).second.m_trustlisttrustcomment;\r
392 \r
393                                 SendBufferedLine(tempstr.str());\r
394                         }\r
395                         SendBufferedLine(".");\r
396                 }\r
397                 else\r
398                 {\r
399                         SendBufferedLine("501 Syntax error");\r
400                 }\r
401         }\r
402         else\r
403         {\r
404                 SendBufferedLine("480 User not authenticated");\r
405         }\r
406         return true;\r
407 }\r
408 \r
409 const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)\r
410 {\r
411         if(command.m_arguments.size()==1)\r
412         {\r
413                 Board board;\r
414                 if(board.Load(command.m_arguments[0])==true)\r
415                 {\r
416                         std::ostringstream tempstr;\r
417 \r
418                         tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
419 \r
420                         SendBufferedLine(tempstr.str());\r
421 \r
422                         // set the current boardid to this one\r
423                         m_status.m_boardid=board.GetBoardID();\r
424                         //set the first message id, -1 if there are no messages\r
425                         board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
426 \r
427                 }\r
428                 else\r
429                 {\r
430                         SendBufferedLine("411 No such newsgroup");\r
431                 }\r
432         }\r
433         else\r
434         {\r
435                 SendBufferedLine("501 Syntax error");\r
436                 m_log->debug("NNTPConnection::HandleGroupCommand syntax error");\r
437         }\r
438 \r
439         return true;\r
440 }\r
441 \r
442 const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)\r
443 {\r
444         \r
445         SendArticleParts(command);\r
446 \r
447         return true;\r
448 }\r
449 \r
450 const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)\r
451 {\r
452         SendBufferedLine("100 Help text follows");\r
453         SendBufferedLine("There is no help text");\r
454         SendBufferedLine(".");\r
455 \r
456         return true;\r
457 }\r
458 \r
459 const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)\r
460 {\r
461         if(m_status.m_boardid!=-1)\r
462         {\r
463                 if(m_status.m_messageid!=-1)\r
464                 {\r
465                         Message mess;\r
466 \r
467                         if(mess.LoadPrevious(m_status.m_messageid,m_status.m_boardid))\r
468                         {\r
469                                 std::ostringstream tempstr;\r
470 \r
471                                 m_status.m_messageid=mess.GetMessageID();\r
472 \r
473                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
474 \r
475                                 SendBufferedLine(tempstr.str());\r
476 \r
477                         }\r
478                         else\r
479                         {\r
480                                 SendBufferedLine("422 No previous article in this group");\r
481                         }\r
482                 }\r
483                 else\r
484                 {\r
485                         SendBufferedLine("420 Current article number is invalid");\r
486                 }\r
487         }\r
488         else\r
489         {\r
490                 SendBufferedLine("412 No newsgroup selected");\r
491         }\r
492 \r
493         return true;\r
494 }\r
495 \r
496 const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)\r
497 {\r
498 \r
499         int type=1;     // default LIST type is active\r
500         std::string arg1="";\r
501         std::string arg2="";\r
502 \r
503         // type of LIST\r
504         if(command.m_arguments.size()>0)\r
505         {\r
506                 StringFunctions::UpperCase(command.m_arguments[0],arg1);\r
507                 if(arg1=="ACTIVE")\r
508                 {\r
509                         type=1;\r
510                 }\r
511                 else if(arg1=="NEWSGROUPS")\r
512                 {\r
513                         type=2;\r
514                 }\r
515                 else if(arg1=="OVERVIEW.FMT")\r
516                 {\r
517                         type=3;\r
518                 }\r
519                 else\r
520                 {\r
521                         type=0;\r
522                 }\r
523         }\r
524         // wildmat\r
525         if(command.m_arguments.size()>1)\r
526         {\r
527                 arg2=command.m_arguments[1];\r
528         }\r
529 \r
530         // LIST ACTIVE [wildmat]\r
531         if(type==1)\r
532         {\r
533                 bool show;\r
534                 std::ostringstream tempstr;\r
535                 BoardList bl;\r
536                 bl.Load();\r
537 \r
538                 SendBufferedLine("215 list of newsgroups follows");\r
539 \r
540                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
541                 {\r
542                         show=true;\r
543                         tempstr.str("");\r
544 \r
545                         // check wilmat match\r
546                         if(arg2!="")\r
547                         {\r
548                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
549                         }\r
550 \r
551                         if(show==true && (*i).GetSaveReceivedMessages()==true)\r
552                         {\r
553                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");\r
554                                 SendBufferedLine(tempstr.str());\r
555                         }\r
556                 }\r
557 \r
558                 SendBufferedLine(".");\r
559 \r
560         }\r
561         // LIST NEWSGROUPS\r
562         else if(type==2)\r
563         {\r
564                 bool show;\r
565                 std::ostringstream tempstr;\r
566                 BoardList bl;\r
567                 bl.Load();\r
568 \r
569                 SendBufferedLine("215 list of newsgroups follows");\r
570 \r
571                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
572                 {\r
573                         show=true;\r
574                         tempstr.str("");\r
575 \r
576                         // check wilmat match\r
577                         if(arg2!="")\r
578                         {\r
579                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
580                         }\r
581 \r
582                         if(show==true && (*i).GetSaveReceivedMessages()==true)\r
583                         {\r
584                                 tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();\r
585                                 SendBufferedLine(tempstr.str());\r
586                         }\r
587                 }\r
588 \r
589                 SendBufferedLine(".");\r
590 \r
591         }\r
592         // LIST OVERVIEW.FMT\r
593         else if(type==3)\r
594         {\r
595                 SendBufferedLine("215 Order of fields in overview database.");\r
596                 SendBufferedLine("Subject:");\r
597                 SendBufferedLine("From:");\r
598                 SendBufferedLine("Date:");\r
599                 SendBufferedLine("Message-ID:");\r
600                 SendBufferedLine("References:");\r
601                 SendBufferedLine(":bytes");\r
602                 SendBufferedLine(":lines");\r
603                 SendBufferedLine(".");\r
604         }\r
605         else\r
606         {\r
607                 // unknown arg\r
608                 SendBufferedLine("501 Syntax error");\r
609                 m_log->debug("NNTPConnection::HandleListCommand unhandled LIST variant");\r
610         }\r
611 \r
612         return true;\r
613 }\r
614 \r
615 const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)\r
616 {\r
617 \r
618         std::ostringstream tempstr;\r
619         Board board;\r
620         bool validgroup=false;\r
621         int lownum=-1;\r
622         int highnum=-1;\r
623 \r
624         // no args and invalid boardid\r
625         if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
626         {\r
627                 SendBufferedLine("412 No newsgroup selected");\r
628         }\r
629         else if(command.m_arguments.size()==0)\r
630         {\r
631                 validgroup=board.Load(m_status.m_boardid);\r
632         }\r
633         else if(command.m_arguments.size()==1)\r
634         {\r
635                 validgroup=board.Load(command.m_arguments[0]);\r
636                 if(validgroup)\r
637                 {\r
638                         lownum=board.GetLowMessageID();\r
639                         highnum=board.GetHighMessageID();\r
640                 }\r
641                 else\r
642                 {\r
643                         SendBufferedLine("411 No such newsgroup");\r
644                 }\r
645         }\r
646         else if(command.m_arguments.size()==2)\r
647         {\r
648                 validgroup=board.Load(command.m_arguments[0]);\r
649                 std::vector<std::string> rangeparts;\r
650                 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
651 \r
652                 if(rangeparts.size()>0)\r
653                 {\r
654                         StringFunctions::Convert(rangeparts[0],lownum);\r
655                 }\r
656                 if(rangeparts.size()>1)\r
657                 {\r
658                         StringFunctions::Convert(rangeparts[1],highnum);\r
659                 }\r
660 \r
661         }\r
662         else\r
663         {\r
664                 // unknown arg\r
665                 SendBufferedLine("501 Syntax error");\r
666                 m_log->debug("NNTPConnection::HandleListGroupCommand unknown arguments");\r
667         }\r
668 \r
669         if(validgroup)\r
670         {\r
671 \r
672                 // set boardid and messageid\r
673                 m_status.m_boardid=board.GetBoardID();\r
674                 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
675 \r
676                 if(lownum==-1)\r
677                 {\r
678                         lownum=board.GetLowMessageID();\r
679                 }\r
680                 if(highnum==-1)\r
681                 {\r
682                         highnum=board.GetHighMessageID();\r
683                 }\r
684 \r
685                 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
686                 SendBufferedLine(tempstr.str());\r
687 \r
688                 MessageList ml;\r
689                 ml.LoadRange(lownum,highnum,board.GetBoardID());\r
690 \r
691                 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
692                 {\r
693                         tempstr.str("");\r
694                         tempstr << (*i).GetMessageID();\r
695 \r
696                         SendBufferedLine(tempstr.str());\r
697                 }\r
698 \r
699                 // end of multi-line response\r
700                 SendBufferedLine(".");\r
701 \r
702         }\r
703 \r
704         return true;\r
705 }\r
706 \r
707 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
708 {\r
709         if(command.m_arguments.size()>0)\r
710         {\r
711                 std::string arg=command.m_arguments[0];\r
712                 StringFunctions::UpperCase(arg,arg);\r
713                 if(arg=="READER")\r
714                 {\r
715                         m_status.m_mode=MODE_READER;\r
716                         if(m_status.m_allowpost==true)\r
717                         {\r
718                                 SendBufferedLine("200 Posting allowed");\r
719                         }\r
720                         else\r
721                         {\r
722                                 SendBufferedLine("201 Posting prohibited");\r
723                         }\r
724                         \r
725                         m_log->debug("NNTPConnection::HandleModeCommand set mode to reader");\r
726                 }\r
727                 else\r
728                 {\r
729                         SendBufferedLine("501 Syntax error");\r
730                         m_log->debug("NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
731                 }\r
732         }\r
733         else\r
734         {\r
735                 SendBufferedLine("501 Syntax error");\r
736                 m_log->debug("NNTPConnection::HandleModeCommand no argument supplied for MODE");        \r
737         }\r
738 \r
739         return true;\r
740 }\r
741 \r
742 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
743 {\r
744         if(command.m_arguments.size()>=2)\r
745         {\r
746                 Poco::DateTime date;\r
747                 int tempyear=0;\r
748                 int tempmonth=0;\r
749                 int tempday=0;\r
750                 if(command.m_arguments[0].size()==8)\r
751                 {\r
752                         StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempyear);\r
753                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempmonth);\r
754                         StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempday);\r
755                         try\r
756                         {\r
757                                 date.assign(tempyear,tempmonth,tempday,date.hour(),date.minute(),date.second());\r
758                         }\r
759                         catch(...)\r
760                         {\r
761                                 m_log->fatal("NNTPConnection::HandleNewGroupsCommand error assigning date");\r
762                         }\r
763                 }\r
764                 else\r
765                 {\r
766                         /*\r
767                         from RFC 3977\r
768                         If the first two digits of the year are not specified\r
769                         (this is supported only for backward compatibility), the year is to\r
770                         be taken from the current century if yy is smaller than or equal to\r
771                         the current year, and the previous century otherwise.\r
772                         */\r
773                         int century;\r
774                         Poco::DateTime now;\r
775                         century=now.year()-(now.year()%100);\r
776 \r
777                         StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempyear);\r
778                         tempyear<=now.year()-century ? tempyear+=century : tempyear+=(century-100);\r
779                         \r
780                         //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
781                         \r
782                         StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempmonth);\r
783                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempday);\r
784                         try\r
785                         {\r
786                                 date.assign(tempyear,tempmonth,tempday);\r
787                         }\r
788                         catch(...)\r
789                         {\r
790                                 m_log->fatal("NNTPConnection::HandleNewGroupsCommand error assigning date");\r
791                         }\r
792                 }\r
793 \r
794                 BoardList bl;\r
795 \r
796                 bl.LoadNew(Poco::DateTimeFormatter::format(date,"%Y-%m-%d %H:%M:%S"));\r
797 \r
798                 SendBufferedLine("231 List of new newsgroups follows");\r
799 \r
800                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
801                 {\r
802                         if((*i).GetSaveReceivedMessages()==true)\r
803                         {\r
804                                 std::ostringstream tempstr;\r
805                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
806                                 SendBufferedLine(tempstr.str());\r
807                         }\r
808                 }\r
809 \r
810                 SendBufferedLine(".");\r
811 \r
812         }\r
813         else\r
814         {\r
815                 SendBufferedLine("501 Syntax error");\r
816                 m_log->debug("NNTPConnection::HandleNewGroupsCommand syntax error");\r
817         }\r
818 \r
819         return true;\r
820 \r
821 }\r
822 \r
823 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
824 {\r
825         if(m_status.m_boardid!=-1)\r
826         {\r
827                 if(m_status.m_messageid!=-1)\r
828                 {\r
829                         Message mess;\r
830 \r
831                         if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))\r
832                         {\r
833                                 std::ostringstream tempstr;\r
834 \r
835                                 m_status.m_messageid=mess.GetMessageID();\r
836 \r
837                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
838 \r
839                                 SendBufferedLine(tempstr.str());\r
840 \r
841                         }\r
842                         else\r
843                         {\r
844                                 SendBufferedLine("421 No next article in this group");\r
845                         }\r
846                 }\r
847                 else\r
848                 {\r
849                         SendBufferedLine("420 Current article number is invalid");\r
850                 }\r
851         }\r
852         else\r
853         {\r
854                 SendBufferedLine("412 No newsgroup selected");\r
855         }\r
856 \r
857         return true;\r
858 \r
859 }\r
860 \r
861 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
862 {\r
863         long lowmessageid,highmessageid;\r
864         std::string messageuuid="";\r
865 \r
866         lowmessageid=highmessageid=-2;\r
867 \r
868         if(command.m_arguments.size()==0)\r
869         {\r
870                 lowmessageid=m_status.m_messageid;\r
871                 highmessageid=m_status.m_messageid;\r
872         }\r
873         else\r
874         {\r
875                 // Message-ID\r
876                 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
877                 {\r
878                         messageuuid=command.m_arguments[0];\r
879                         messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
880                         messageuuid=StringFunctions::Replace(messageuuid,">","");\r
881                         /*\r
882                         // get rid of @ and everything after\r
883                         if(messageuuid.find("@")!=std::string::npos)\r
884                         {\r
885                                 messageuuid.erase(messageuuid.find("@"));\r
886                         }\r
887                         */\r
888                 }\r
889                 // single article or range\r
890                 else\r
891                 {\r
892                         // range\r
893                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
894                         {\r
895                                 std::vector<std::string> rangeparts;\r
896                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
897                                 // no upper bound\r
898                                 if(rangeparts.size()>0)\r
899                                 {\r
900                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
901                                         highmessageid=-1;\r
902                                 }\r
903                                 //upper bound\r
904                                 else if(rangeparts.size()>1)\r
905                                 {\r
906                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
907                                 }\r
908                         }\r
909                         // single\r
910                         else\r
911                         {\r
912                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
913                         }\r
914                 }\r
915         }\r
916 \r
917         if(messageuuid!="")\r
918         {\r
919                 Message mess;\r
920                 if(mess.Load(messageuuid))\r
921                 {\r
922                         SendBufferedLine("224 Overview information follows");\r
923                         SendArticleOverInfo(mess);\r
924                         SendBufferedLine(".");\r
925                 }\r
926                 else\r
927                 {\r
928                         SendBufferedLine("423 No such article");\r
929                 }\r
930         }\r
931         else\r
932         {\r
933                 Board bd;\r
934                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
935                 {\r
936                         // single message\r
937                         if(highmessageid==-2)\r
938                         {\r
939                                 Message mess;\r
940                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
941                                 {\r
942                                         SendBufferedLine("224 Overview information follows");\r
943                                         SendArticleOverInfo(mess);\r
944                                         SendBufferedLine(".");\r
945                                 }\r
946                                 else\r
947                                 {\r
948                                         SendBufferedLine("423 No such article in this group");\r
949                                 }\r
950                         }\r
951                         // range with no upper bound\r
952                         else if(highmessageid==-1)\r
953                         {\r
954                                 MessageList ml;\r
955                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
956                                 if(ml.size()>0)\r
957                                 {\r
958                                         SendBufferedLine("224 Overview information follows");\r
959                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
960                                         {\r
961                                                 SendArticleOverInfo((*i));\r
962                                         }\r
963                                         SendBufferedLine(".");\r
964                                 }\r
965                                 else\r
966                                 {\r
967                                         SendBufferedLine("423 Empty range");\r
968                                 }\r
969                         }\r
970                         // range with upper and lower bound\r
971                         else if(highmessageid>=lowmessageid)\r
972                         {\r
973                                 MessageList ml;\r
974                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
975                                 if(ml.size()>0)\r
976                                 {\r
977                                         SendBufferedLine("224 Overview information follows");\r
978                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
979                                         {\r
980                                                 SendArticleOverInfo((*i));\r
981                                         }\r
982                                         SendBufferedLine(".");\r
983                                 }\r
984                                 else\r
985                                 {\r
986                                         SendBufferedLine("423 Empty range");\r
987                                 }\r
988                         }\r
989                         // invalid range\r
990                         else\r
991                         {\r
992                                 SendBufferedLine("423 Empty range");\r
993                         }\r
994                 }\r
995                 else\r
996                 {\r
997                         SendBufferedLine("423 No newsgroup selected");\r
998                 }\r
999         }\r
1000 \r
1001         return true;\r
1002 \r
1003 }\r
1004 \r
1005 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
1006 {\r
1007         if(m_status.m_allowpost==true)\r
1008         {\r
1009                 SendBufferedLine("340 Send article to be posted");\r
1010                 m_status.m_isposting=true;\r
1011         }\r
1012         else\r
1013         {\r
1014                 SendBufferedLine("440 Posting not permitted");\r
1015         }\r
1016 \r
1017         return true;\r
1018 }\r
1019 \r
1020 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
1021 {\r
1022         Message mess;\r
1023 \r
1024         if(mess.ParseNNTPMessage(message))\r
1025         {\r
1026                 // if we authenticated, set the username to the authenticated user\r
1027                 if(m_status.m_authenticated)\r
1028                 {\r
1029                         mess.SetFromName(m_status.m_authuser.GetName());\r
1030                 }\r
1031                 // handle a messages posted to an adminboard\r
1032                 if(mess.PostedToAdministrationBoard()==true)\r
1033                 {\r
1034                         mess.HandleAdministrationMessage();\r
1035                 }\r
1036                 if(mess.StartFreenetInsert())\r
1037                 {\r
1038                         SendBufferedLine("240 Article received OK");\r
1039                 }\r
1040                 else\r
1041                 {\r
1042                         SendBufferedLine("441 Posting failed.  Make sure the identity you are sending with exists!");\r
1043                 }\r
1044         }\r
1045         else\r
1046         {\r
1047                 SendBufferedLine("441 Posting failed");\r
1048         }\r
1049 }\r
1050 \r
1051 void NNTPConnection::HandleReceivedData()\r
1052 {\r
1053         if(m_status.m_isposting==false)\r
1054         {\r
1055                 // get end of command line\r
1056                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
1057                 \r
1058                 // we got a command\r
1059                 if(endpos!=m_receivebuffer.end())\r
1060                 {\r
1061                         NNTPCommand command;\r
1062                         std::string commandline(m_receivebuffer.begin(),endpos);\r
1063 \r
1064                         // remove command from receive buffer\r
1065                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
1066 \r
1067                         // remove any leading/trailing whitespace\r
1068                         commandline=StringFunctions::TrimWhitespace(commandline);\r
1069 \r
1070                         // split out command and arguments separated by space or tab\r
1071                         StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
1072 \r
1073                         // command is first element in argument vector\r
1074                         command.m_command=command.m_arguments[0];\r
1075                         // erase command from argument vector and make it upper case\r
1076                         command.m_arguments.erase(command.m_arguments.begin());\r
1077                         StringFunctions::UpperCase(command.m_command,command.m_command);\r
1078 \r
1079                         if(HandleCommand(command)==true)\r
1080                         {\r
1081                                 \r
1082                         }\r
1083                         else\r
1084                         {\r
1085                                 SendBufferedLine("500 Unknown command");\r
1086 \r
1087                                 m_log->debug("NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
1088                         }\r
1089 \r
1090                 }\r
1091 \r
1092         }\r
1093         else\r
1094         {\r
1095                 // check for end of post\r
1096                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
1097 \r
1098                 if(endpos!=m_receivebuffer.end())\r
1099                 {\r
1100                         // get the message\r
1101                         std::string message(m_receivebuffer.begin(),endpos);\r
1102                         // remove from receive buffer\r
1103                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
1104 \r
1105                         // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
1106                         message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
1107 \r
1108                         HandlePostedMessage(message);\r
1109 \r
1110                         // message was received, so posting is completed\r
1111                         m_status.m_isposting=false;\r
1112 \r
1113                 }\r
1114         }\r
1115 }\r
1116 \r
1117 const bool NNTPConnection::HandleSetTrustCommand(const NNTPCommand &command)\r
1118 {\r
1119         if(command.m_arguments.size()>=3)\r
1120         {\r
1121                 std::string type=command.m_arguments[0];\r
1122                 StringFunctions::UpperCase(type,type);\r
1123                 if(type=="MESSAGE" || type=="TRUSTLIST" || type=="MESSAGECOMMENT" || type=="TRUSTLISTCOMMENT")\r
1124                 {\r
1125                         if(m_status.m_authenticated)\r
1126                         {\r
1127                                 bool found=false;\r
1128                                 bool valid=false;\r
1129                                 int trust=-1;\r
1130                                 std::string comment="";\r
1131                                 std::string nntpname="";\r
1132 \r
1133                                 if(type=="MESSAGE" || type=="TRUSTLIST")\r
1134                                 {\r
1135                                         for(int i=1; i<command.m_arguments.size()-1; i++)\r
1136                                         {\r
1137                                                 if(i!=1)\r
1138                                                 {\r
1139                                                         nntpname+=" ";\r
1140                                                 }\r
1141                                                 nntpname+=command.m_arguments[i];\r
1142                                         }\r
1143 \r
1144                                         if(command.m_arguments[command.m_arguments.size()-1]!="null")\r
1145                                         {\r
1146                                                 StringFunctions::Convert(command.m_arguments[command.m_arguments.size()-1],trust);\r
1147                                         }\r
1148 \r
1149                                         if(trust>=-1 && trust<=100)\r
1150                                         {\r
1151                                                 valid=true;\r
1152                                         }\r
1153                                 }\r
1154                                 else\r
1155                                 {\r
1156                                         int startpos=-1;\r
1157                                         // get nntpname\r
1158                                         for(int i=1; i<command.m_arguments.size() && startpos==-1; i++)\r
1159                                         {\r
1160                                                 if(command.m_arguments[i].size()>0 && command.m_arguments[i][0]!='\"')\r
1161                                                 {\r
1162                                                         if(i!=1)\r
1163                                                         {\r
1164                                                                 nntpname+=" ";\r
1165                                                         }\r
1166                                                         nntpname+=command.m_arguments[i];\r
1167                                                 }\r
1168                                                 else\r
1169                                                 {\r
1170                                                         startpos=i;\r
1171                                                 }\r
1172                                         }\r
1173 \r
1174                                         // get comment\r
1175                                         for(int i=startpos; i<command.m_arguments.size(); i++)\r
1176                                         {\r
1177                                                 if(i!=startpos)\r
1178                                                 {\r
1179                                                         comment+=" ";\r
1180                                                 }\r
1181                                                 comment+=command.m_arguments[i];\r
1182                                         }\r
1183                                         // strip " from comment beginning and end\r
1184                                         if(comment.size()>0 && comment[0]=='\"')\r
1185                                         {\r
1186                                                 comment.erase(0,1);\r
1187                                         }\r
1188                                         if(comment.size()>0 && comment[comment.size()-1]=='\"')\r
1189                                         {\r
1190                                                 comment.erase(comment.size()-1);\r
1191                                         }\r
1192 \r
1193                                         valid=true;\r
1194                                 }\r
1195 \r
1196                                 TrustExtension tr(m_status.m_authuser.GetID());\r
1197 \r
1198                                 if(type=="MESSAGE")\r
1199                                 {\r
1200                                         if(tr.SetMessageTrust(nntpname,trust))\r
1201                                         {\r
1202                                                 found=true;\r
1203                                         }\r
1204                                 }\r
1205                                 if(type=="TRUSTLIST")\r
1206                                 {\r
1207                                         if(tr.SetTrustListTrust(nntpname,trust))\r
1208                                         {\r
1209                                                 found=true;\r
1210                                         }\r
1211                                 }\r
1212                                 if(type=="MESSAGECOMMENT")\r
1213                                 {\r
1214                                         if(tr.SetMessageTrustComment(nntpname,comment))\r
1215                                         {\r
1216                                                 found=true;\r
1217                                         }\r
1218                                 }\r
1219                                 if(type=="TRUSTLISTCOMMENT")\r
1220                                 {\r
1221                                         if(tr.SetTrustListTrustComment(nntpname,comment))\r
1222                                         {\r
1223                                                 found=true;\r
1224                                         }\r
1225                                 }\r
1226 \r
1227                                 if(found && valid)\r
1228                                 {\r
1229                                         SendBufferedLine("280 Trust Set");\r
1230                                 }\r
1231                                 else if(found==false)\r
1232                                 {\r
1233                                         SendBufferedLine("480 Identity not found");\r
1234                                 }\r
1235                                 else\r
1236                                 {\r
1237                                         SendBufferedLine("501 Syntax error");\r
1238                                 }\r
1239 \r
1240                         }\r
1241                         else\r
1242                         {\r
1243                                 SendBufferedLine("480 User not authenticated");\r
1244                         }\r
1245                 }\r
1246                 else\r
1247                 {\r
1248                         SendBufferedLine("501 Syntax error");\r
1249                 }\r
1250         }\r
1251         else\r
1252         {\r
1253                 SendBufferedLine("501 Syntax error");\r
1254         }\r
1255         return true;\r
1256 }\r
1257 \r
1258 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
1259 {\r
1260         SendArticleParts(command);\r
1261 \r
1262         return true;\r
1263 }\r
1264 \r
1265 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
1266 {\r
1267         SendBufferedLine("205 Connection Closing");\r
1268         SocketSend();\r
1269         Disconnect();\r
1270         m_log->information("NNTPConnection::HandleQuitCommand client closed connection");\r
1271         return true;\r
1272 }\r
1273 \r
1274 void NNTPConnection::run()\r
1275 {\r
1276         struct timeval tv;\r
1277         fd_set writefs,readfs;\r
1278         int rval;\r
1279 \r
1280         // seed random number generater for this thread\r
1281         srand(time(NULL));\r
1282         \r
1283         if(m_status.m_allowpost==true)\r
1284         {\r
1285                 SendBufferedLine("200 Service available, posting allowed");\r
1286         }\r
1287         else\r
1288         {\r
1289                 SendBufferedLine("201 Service available, posting prohibited");\r
1290         }\r
1291 \r
1292         do\r
1293         {\r
1294                 FD_ZERO(&readfs);\r
1295                 FD_ZERO(&writefs);\r
1296                 \r
1297                 FD_SET(m_socket,&readfs);\r
1298                 if(m_sendbuffer.size()>0)\r
1299                 {\r
1300                         FD_SET(m_socket,&writefs);\r
1301                 }\r
1302                 \r
1303                 tv.tv_sec=1;\r
1304                 tv.tv_usec=0;\r
1305                 \r
1306                 rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
1307                 \r
1308                 if(rval>0)\r
1309                 {\r
1310                         if(FD_ISSET(m_socket,&readfs))\r
1311                         {\r
1312                                 SocketReceive();\r
1313                                 HandleReceivedData();\r
1314                         }\r
1315                         if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
1316                         {\r
1317                                 SocketSend();\r
1318                         }\r
1319                 }\r
1320                 else if(rval==SOCKET_ERROR)\r
1321                 {\r
1322                         m_log->error("NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());      \r
1323                 }\r
1324 \r
1325                 //process all remaining commands in buffer\r
1326                 std::vector<char>::size_type rbs=0;\r
1327                 while(rbs!=m_receivebuffer.size())\r
1328                 {\r
1329                         rbs=m_receivebuffer.size();\r
1330                         HandleReceivedData();\r
1331                 }\r
1332 \r
1333         }while(!Disconnected() && !IsCancelled());\r
1334 \r
1335         Disconnect();\r
1336 \r
1337 }\r
1338 \r
1339 void NNTPConnection::SendArticleOverInfo(Message &message)\r
1340 {\r
1341         std::string tempval;\r
1342         std::string line;\r
1343         std::map<long,std::string> references;\r
1344 \r
1345         StringFunctions::Convert(message.GetMessageID(),tempval);\r
1346         line=tempval+"\t";\r
1347         line+=message.GetSubject()+"\t";\r
1348         line+=message.GetFromName()+"\t";\r
1349         line+=Poco::DateTimeFormatter::format(message.GetDateTime(),"%w, %d %b %y %H:%M:%S -0000")+"\t";\r
1350         line+=message.GetNNTPArticleID()+"\t";\r
1351         references=message.GetInReplyTo();\r
1352         if(references.size()>0)\r
1353         {\r
1354                 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
1355                 {\r
1356                         if(i!=references.rbegin())\r
1357                         {\r
1358                                 line+=" ";\r
1359                         }\r
1360                         line+="<"+(*i).second+">"; //+"@freenetproject.org>";\r
1361                 }\r
1362                 line+="\t";\r
1363         }\r
1364         else\r
1365         {\r
1366                 line+="\t";\r
1367         }\r
1368         line+="\t";\r
1369 \r
1370         SendBufferedLine(line);\r
1371 }\r
1372 \r
1373 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
1374 {\r
1375         bool sendheaders,sendbody;\r
1376         std::string successcode;\r
1377 \r
1378         if(command.m_command=="ARTICLE")\r
1379         {\r
1380                 sendheaders=true;\r
1381                 sendbody=true;\r
1382                 successcode="220";\r
1383         }\r
1384         else if(command.m_command=="HEAD")\r
1385         {\r
1386                 sendheaders=true;\r
1387                 sendbody=false;\r
1388                 successcode="221";\r
1389         }\r
1390         else if(command.m_command=="BODY")\r
1391         {\r
1392                 sendheaders=false;\r
1393                 sendbody=true;\r
1394                 successcode="222";\r
1395         }\r
1396         else if(command.m_command=="STAT")\r
1397         {\r
1398                 sendheaders=false;\r
1399                 sendbody=false;\r
1400                 successcode="223";\r
1401         }\r
1402 \r
1403         Message message;\r
1404         int messageid=m_status.m_messageid;\r
1405         std::string articleid="";\r
1406         int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
1407 \r
1408         if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
1409         {\r
1410                 if(command.m_arguments[0].find("<")==std::string::npos)\r
1411                 {\r
1412                         StringFunctions::Convert(command.m_arguments[0],messageid);\r
1413                         message.Load(messageid,m_status.m_boardid);\r
1414                         m_status.m_messageid=message.GetMessageID();\r
1415                         type=1;\r
1416                 }\r
1417                 else\r
1418                 {\r
1419                         articleid=command.m_arguments[0];\r
1420                         //strip off < and > and everthing after @\r
1421                         if(articleid.size()>0 && articleid[0]=='<')\r
1422                         {\r
1423                                 articleid.erase(0,1);\r
1424                         }\r
1425                         if(articleid.size()>0 && articleid[articleid.size()-1]=='>')\r
1426                         {\r
1427                                 articleid.erase(articleid.size()-1);\r
1428                         }\r
1429                         /*\r
1430                         if(articleid.size()>0 && articleid.find('@')!=std::string::npos)\r
1431                         {\r
1432                                 articleid.erase(articleid.find('@'));\r
1433                         }\r
1434                         */\r
1435                         message.Load(articleid);\r
1436                         type=2;\r
1437                 }\r
1438         }\r
1439         else\r
1440         {\r
1441                 message.Load(m_status.m_messageid,m_status.m_boardid);\r
1442         }\r
1443 \r
1444         switch(type)\r
1445         {\r
1446         case 0:\r
1447                 if(m_status.m_boardid!=-1)\r
1448                 {\r
1449                         if(m_status.m_messageid!=-1)\r
1450                         {\r
1451                                 std::ostringstream tempstr;\r
1452                                 std::string article;\r
1453                                 if(sendheaders&&sendbody)\r
1454                                 {\r
1455                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1456                                 }\r
1457                                 else if(sendheaders && !sendbody)\r
1458                                 {\r
1459                                         article=message.GetNNTPHeaders();\r
1460                                         // strip off final \r\n from headers\r
1461                                         if(article.rfind("\r\n")==article.size()-2)\r
1462                                         {\r
1463                                                 article.erase(article.size()-2);\r
1464                                         }\r
1465                                 }\r
1466                                 else\r
1467                                 {\r
1468                                         article=message.GetNNTPBody();\r
1469                                 }\r
1470                                 // dot stuff article\r
1471                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1472 \r
1473                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1474 \r
1475                                 SendBufferedLine(tempstr.str());\r
1476                                 if(sendheaders || sendbody)\r
1477                                 {\r
1478                                         SendBufferedLine(article);\r
1479                                         SendBufferedLine(".");\r
1480                                 }\r
1481 \r
1482                         }\r
1483                         else\r
1484                         {\r
1485                                 SendBufferedLine("420 Current article number is invalid");\r
1486                         }\r
1487                 }\r
1488                 else\r
1489                 {\r
1490                         SendBufferedLine("412 No newsgroup selected");\r
1491                 }\r
1492                 break;\r
1493         case 1:\r
1494                 if(m_status.m_boardid!=-1)\r
1495                 {\r
1496                         if(message.GetMessageID()!=-1)\r
1497                         {\r
1498                                 std::ostringstream tempstr;\r
1499                                 std::string article;\r
1500                                 if(sendheaders&&sendbody)\r
1501                                 {\r
1502                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1503                                 }\r
1504                                 else if(sendheaders && !sendbody)\r
1505                                 {\r
1506                                         article=message.GetNNTPHeaders();\r
1507                                         // strip off final \r\n from headers\r
1508                                         if(article.rfind("\r\n")==article.size()-2)\r
1509                                         {\r
1510                                                 article.erase(article.size()-2);\r
1511                                         }\r
1512                                 }\r
1513                                 else\r
1514                                 {\r
1515                                         article=message.GetNNTPBody();\r
1516                                 }\r
1517                                 // dot stuff article\r
1518                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1519 \r
1520                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1521 \r
1522                                 SendBufferedLine(tempstr.str());\r
1523                                 if(sendheaders || sendbody)\r
1524                                 {\r
1525                                         SendBufferedLine(article);\r
1526                                         SendBufferedLine(".");\r
1527                                 }\r
1528                         }\r
1529                         else\r
1530                         {\r
1531                                 SendBufferedLine("423 No article with that number");\r
1532                         }\r
1533                 }\r
1534                 else\r
1535                 {\r
1536                         SendBufferedLine("412 No newsgroup selected");\r
1537                 }\r
1538                 break;\r
1539         case 2:\r
1540                 if(message.GetMessageID()!=-1)\r
1541                 {\r
1542                         std::string article;\r
1543                         if(sendheaders&&sendbody)\r
1544                         {\r
1545                                 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1546                         }\r
1547                         else if(sendheaders && !sendbody)\r
1548                         {\r
1549                                 article=message.GetNNTPHeaders();\r
1550                                 // strip off final \r\n from headers\r
1551                                 if(article.rfind("\r\n")==article.size()-2)\r
1552                                 {\r
1553                                         article.erase(article.size()-2);\r
1554                                 }\r
1555                         }\r
1556                         else\r
1557                         {\r
1558                                 article=message.GetNNTPBody();\r
1559                         }\r
1560                         // dot stuff article\r
1561                         article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1562 \r
1563                         SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
1564                         if(sendheaders || sendbody)\r
1565                         {\r
1566                                 SendBufferedLine(article);\r
1567                                 SendBufferedLine(".");\r
1568                         }\r
1569                 }\r
1570                 else\r
1571                 {\r
1572                         SendBufferedLine("430 No article with that message-id");\r
1573                 }\r
1574                 break;\r
1575         }\r
1576 \r
1577 }\r
1578 \r
1579 void NNTPConnection::SendBuffered(const std::string &data)\r
1580 {\r
1581         m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
1582 }\r
1583 \r
1584 void NNTPConnection::SocketReceive()\r
1585 {\r
1586         int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
1587         if(rval>0)\r
1588         {\r
1589                 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
1590         }\r
1591         else if(rval==0)\r
1592         {\r
1593                 Disconnect();\r
1594                 m_log->information("NNTPConnection::SocketReceive remote host closed connection");\r
1595         }\r
1596         else if(rval==-1)\r
1597         {\r
1598                 std::string errnostr;\r
1599                 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1600                 // error on receive - close the connection\r
1601                 Disconnect();\r
1602                 m_log->error("NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1603         }\r
1604 }\r
1605 \r
1606 void NNTPConnection::SocketSend()\r
1607 {\r
1608         if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
1609         {\r
1610                 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
1611                 if(rval>0)\r
1612                 {       \r
1613                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
1614                 }\r
1615                 else if(rval==-1)\r
1616                 {\r
1617                         std::string errnostr;\r
1618                         StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1619                         m_log->error("NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1620                 }\r
1621         }\r
1622 }\r