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