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