99914cd3cea7694b093d0439ed3dfbde66917cfa
[fms.git] / src / nntp / nntpconnection.cpp
1 #include "../../include/nntp/nntpconnection.h"\r
2 #include "../../include/nntp/uwildmat.h"\r
3 #include "../../include/stringfunctions.h"\r
4 #include "../../include/datetime.h"\r
5 #include "../../include/boardlist.h"\r
6 #include "../../include/message.h"\r
7 #include "../../include/messagelist.h"\r
8 #include "../../include/option.h"\r
9 #include "../../include/nntp/extensiontrust.h"\r
10 \r
11 #include <algorithm>\r
12 \r
13 //#include <zthread/Thread.h>\r
14 #include "../../include/pthreadwrapper/thread.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         DateTime now;\r
251         now.SetToGMTime();\r
252         SendBufferedLine("111 "+now.Format("%Y%m%d%H%M%S"));\r
253         return true;\r
254 }\r
255 \r
256 const bool NNTPConnection::HandleGetTrustCommand(const NNTPCommand &command)\r
257 {\r
258         if(command.m_arguments.size()>=2)\r
259         {\r
260                 std::string type=command.m_arguments[0];\r
261                 StringFunctions::UpperCase(type,type);\r
262                 if(type=="MESSAGE" || type=="TRUSTLIST" || type=="PEERMESSAGE" || type=="PEERTRUSTLIST")\r
263                 {\r
264                         if(m_status.m_authenticated)\r
265                         {\r
266                                 bool found=false;\r
267                                 int trust=-1;\r
268                                 std::string nntpname="";\r
269                                 for(int i=1; i<command.m_arguments.size(); i++)\r
270                                 {\r
271                                         nntpname+=command.m_arguments[i];\r
272                                 }\r
273 \r
274                                 TrustExtension tr(m_status.m_authuser.GetID());\r
275 \r
276                                 if(type=="MESSAGE")\r
277                                 {\r
278                                         if(tr.GetMessageTrust(nntpname,trust))\r
279                                         {\r
280                                                 found=true;\r
281                                         }\r
282                                 }\r
283                                 else if(type=="TRUSTLIST")\r
284                                 {\r
285                                         if(tr.GetTrustListTrust(nntpname,trust))\r
286                                         {\r
287                                                 found=true;\r
288                                         }\r
289                                 }\r
290                                 else if(type=="PEERMESSAGE")\r
291                                 {\r
292                                         if(tr.GetPeerMessageTrust(nntpname,trust))\r
293                                         {\r
294                                                 found=true;\r
295                                         }\r
296                                 }\r
297                                 else if(type=="PEERTRUSTLIST")\r
298                                 {\r
299                                         if(tr.GetPeerTrustListTrust(nntpname,trust))\r
300                                         {\r
301                                                 found=true;\r
302                                         }\r
303                                 }\r
304 \r
305                                 if(trust>=0 && found)\r
306                                 {\r
307                                         std::string truststr="";\r
308                                         StringFunctions::Convert(trust,truststr);\r
309                                         SendBufferedLine("280 "+truststr);\r
310                                 }\r
311                                 else if(found)\r
312                                 {\r
313                                         SendBufferedLine("281 null");\r
314                                 }\r
315                                 else\r
316                                 {\r
317                                         SendBufferedLine("480 Identity not found");\r
318                                 }\r
319 \r
320                         }\r
321                         else\r
322                         {\r
323                                 SendBufferedLine("480 User not authenticated");\r
324                         }\r
325                 }\r
326                 else\r
327                 {\r
328                         SendBufferedLine("501 Syntax error");\r
329                 }\r
330         }\r
331         else\r
332         {\r
333                 SendBufferedLine("501 Syntax error");\r
334         }\r
335         return true;\r
336 }       \r
337 \r
338 const bool NNTPConnection::HandleGetTrustListCommand(const NNTPCommand &command)\r
339 {\r
340         if(m_status.m_authenticated)\r
341         {\r
342                 TrustExtension tr(m_status.m_authuser.GetID());\r
343                 std::map<std::string,std::pair<int,int> > trustlist;\r
344                 if(tr.GetTrustList(trustlist))\r
345                 {\r
346                         SendBufferedLine("280 Trust list follows");\r
347                         for(std::map<std::string,std::pair<int,int> >::iterator i=trustlist.begin(); i!=trustlist.end(); i++)\r
348                         {\r
349                                 std::ostringstream tempstr;\r
350                                 tempstr << (*i).first << "\t";\r
351                                 if((*i).second.first>-1)\r
352                                 {\r
353                                         tempstr << (*i).second.first;\r
354                                 } \r
355                                 else\r
356                                 {\r
357                                         tempstr << "null";\r
358                                 }\r
359                                 tempstr << "\t";\r
360                                 if((*i).second.second>-1)\r
361                                 {\r
362                                         tempstr << (*i).second.second;\r
363                                 }\r
364                                 else\r
365                                 {\r
366                                         tempstr << "null";\r
367                                 }\r
368                                 SendBufferedLine(tempstr.str());\r
369                         }\r
370                         SendBufferedLine(".");\r
371                 }\r
372                 else\r
373                 {\r
374                         SendBufferedLine("501 Syntax error");\r
375                 }\r
376         }\r
377         else\r
378         {\r
379                 SendBufferedLine("480 User not authenticated");\r
380         }\r
381         return true;\r
382 }\r
383 \r
384 const bool NNTPConnection::HandleGroupCommand(const NNTPCommand &command)\r
385 {\r
386         if(command.m_arguments.size()==1)\r
387         {\r
388                 Board board;\r
389                 if(board.Load(command.m_arguments[0])==true)\r
390                 {\r
391                         std::ostringstream tempstr;\r
392 \r
393                         tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
394 \r
395                         SendBufferedLine(tempstr.str());\r
396 \r
397                         // set the current boardid to this one\r
398                         m_status.m_boardid=board.GetBoardID();\r
399                         //set the first message id, -1 if there are no messages\r
400                         board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
401 \r
402                 }\r
403                 else\r
404                 {\r
405                         SendBufferedLine("411 No such newsgroup");\r
406                 }\r
407         }\r
408         else\r
409         {\r
410                 SendBufferedLine("501 Syntax error");\r
411                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleGroupCommand syntax error");\r
412         }\r
413 \r
414         return true;\r
415 }\r
416 \r
417 const bool NNTPConnection::HandleHeadCommand(const NNTPCommand &command)\r
418 {\r
419         \r
420         SendArticleParts(command);\r
421 \r
422         return true;\r
423 }\r
424 \r
425 const bool NNTPConnection::HandleHelpCommand(const NNTPCommand &command)\r
426 {\r
427         SendBufferedLine("100 Help text follows");\r
428         SendBufferedLine("There is no help text");\r
429         SendBufferedLine(".");\r
430 \r
431         return true;\r
432 }\r
433 \r
434 const bool NNTPConnection::HandleLastCommand(const NNTPCommand &command)\r
435 {\r
436         if(m_status.m_boardid!=-1)\r
437         {\r
438                 if(m_status.m_messageid!=-1)\r
439                 {\r
440                         Message mess;\r
441 \r
442                         if(mess.LoadPrevious(m_status.m_messageid,m_status.m_boardid))\r
443                         {\r
444                                 std::ostringstream tempstr;\r
445 \r
446                                 m_status.m_messageid=mess.GetMessageID();\r
447 \r
448                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
449 \r
450                                 SendBufferedLine(tempstr.str());\r
451 \r
452                         }\r
453                         else\r
454                         {\r
455                                 SendBufferedLine("422 No previous article in this group");\r
456                         }\r
457                 }\r
458                 else\r
459                 {\r
460                         SendBufferedLine("420 Current article number is invalid");\r
461                 }\r
462         }\r
463         else\r
464         {\r
465                 SendBufferedLine("412 No newsgroup selected");\r
466         }\r
467 \r
468         return true;\r
469 }\r
470 \r
471 const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)\r
472 {\r
473 \r
474         int type=1;     // default LIST type is active\r
475         std::string arg1="";\r
476         std::string arg2="";\r
477 \r
478         // type of LIST\r
479         if(command.m_arguments.size()>0)\r
480         {\r
481                 StringFunctions::UpperCase(command.m_arguments[0],arg1);\r
482                 if(arg1=="ACTIVE")\r
483                 {\r
484                         type=1;\r
485                 }\r
486                 else if(arg1=="NEWSGROUPS")\r
487                 {\r
488                         type=2;\r
489                 }\r
490                 else if(arg1=="OVERVIEW.FMT")\r
491                 {\r
492                         type=3;\r
493                 }\r
494                 else\r
495                 {\r
496                         type=0;\r
497                 }\r
498         }\r
499         // wildmat\r
500         if(command.m_arguments.size()>1)\r
501         {\r
502                 arg2=command.m_arguments[1];\r
503         }\r
504 \r
505         // LIST ACTIVE [wildmat]\r
506         if(type==1)\r
507         {\r
508                 bool show;\r
509                 std::ostringstream tempstr;\r
510                 BoardList bl;\r
511                 bl.Load();\r
512 \r
513                 SendBufferedLine("215 list of newsgroups follows");\r
514 \r
515                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
516                 {\r
517                         show=true;\r
518                         tempstr.str("");\r
519 \r
520                         // check wilmat match\r
521                         if(arg2!="")\r
522                         {\r
523                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
524                         }\r
525 \r
526                         if(show==true && (*i).GetSaveReceivedMessages()==true)\r
527                         {\r
528                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");\r
529                                 SendBufferedLine(tempstr.str());\r
530                         }\r
531                 }\r
532 \r
533                 SendBufferedLine(".");\r
534 \r
535         }\r
536         // LIST NEWSGROUPS\r
537         else if(type==2)\r
538         {\r
539                 bool show;\r
540                 std::ostringstream tempstr;\r
541                 BoardList bl;\r
542                 bl.Load();\r
543 \r
544                 SendBufferedLine("215 list of newsgroups follows");\r
545 \r
546                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
547                 {\r
548                         show=true;\r
549                         tempstr.str("");\r
550 \r
551                         // check wilmat match\r
552                         if(arg2!="")\r
553                         {\r
554                                 show=uwildmat((*i).GetBoardName().c_str(),arg2.c_str());\r
555                         }\r
556 \r
557                         if(show==true && (*i).GetSaveReceivedMessages()==true)\r
558                         {\r
559                                 tempstr << (*i).GetBoardName() << "\t" << (*i).GetBoardDescription();\r
560                                 SendBufferedLine(tempstr.str());\r
561                         }\r
562                 }\r
563 \r
564                 SendBufferedLine(".");\r
565 \r
566         }\r
567         // LIST OVERVIEW.FMT\r
568         else if(type==3)\r
569         {\r
570                 SendBufferedLine("215 Order of fields in overview database.");\r
571                 SendBufferedLine("Subject:");\r
572                 SendBufferedLine("From:");\r
573                 SendBufferedLine("Date:");\r
574                 SendBufferedLine("Message-ID:");\r
575                 SendBufferedLine("References:");\r
576                 SendBufferedLine(":bytes");\r
577                 SendBufferedLine(":lines");\r
578                 SendBufferedLine(".");\r
579         }\r
580         else\r
581         {\r
582                 // unknown arg\r
583                 SendBufferedLine("501 Syntax error");\r
584                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListCommand unhandled LIST variant");\r
585         }\r
586 \r
587         return true;\r
588 }\r
589 \r
590 const bool NNTPConnection::HandleListGroupCommand(const NNTPCommand &command)\r
591 {\r
592 \r
593         std::ostringstream tempstr;\r
594         Board board;\r
595         bool validgroup=false;\r
596         int lownum=-1;\r
597         int highnum=-1;\r
598 \r
599         // no args and invalid boardid\r
600         if(command.m_arguments.size()==0 && m_status.m_boardid==-1)\r
601         {\r
602                 SendBufferedLine("412 No newsgroup selected");\r
603         }\r
604         else if(command.m_arguments.size()==0)\r
605         {\r
606                 validgroup=board.Load(m_status.m_boardid);\r
607         }\r
608         else if(command.m_arguments.size()==1)\r
609         {\r
610                 validgroup=board.Load(command.m_arguments[0]);\r
611                 if(validgroup)\r
612                 {\r
613                         lownum=board.GetLowMessageID();\r
614                         highnum=board.GetHighMessageID();\r
615                 }\r
616                 else\r
617                 {\r
618                         SendBufferedLine("411 No such newsgroup");\r
619                 }\r
620         }\r
621         else if(command.m_arguments.size()==2)\r
622         {\r
623                 validgroup=board.Load(command.m_arguments[0]);\r
624                 std::vector<std::string> rangeparts;\r
625                 StringFunctions::Split(command.m_arguments[1],"-",rangeparts);\r
626 \r
627                 if(rangeparts.size()>0)\r
628                 {\r
629                         StringFunctions::Convert(rangeparts[0],lownum);\r
630                 }\r
631                 if(rangeparts.size()>1)\r
632                 {\r
633                         StringFunctions::Convert(rangeparts[1],highnum);\r
634                 }\r
635 \r
636         }\r
637         else\r
638         {\r
639                 // unknown arg\r
640                 SendBufferedLine("501 Syntax error");\r
641                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleListGroupCommand unknown arguments");\r
642         }\r
643 \r
644         if(validgroup)\r
645         {\r
646 \r
647                 // set boardid and messageid\r
648                 m_status.m_boardid=board.GetBoardID();\r
649                 board.GetLowMessageID()!=0 ? m_status.m_messageid=board.GetLowMessageID() : m_status.m_messageid=-1;\r
650 \r
651                 if(lownum==-1)\r
652                 {\r
653                         lownum=board.GetLowMessageID();\r
654                 }\r
655                 if(highnum==-1)\r
656                 {\r
657                         highnum=board.GetHighMessageID();\r
658                 }\r
659 \r
660                 tempstr << "211 " << board.GetMessageCount() << " " << board.GetLowMessageID() << " " << board.GetHighMessageID() << " " << board.GetBoardName();\r
661                 SendBufferedLine(tempstr.str());\r
662 \r
663                 MessageList ml;\r
664                 ml.LoadRange(lownum,highnum,board.GetBoardID());\r
665 \r
666                 for(std::vector<Message>::iterator i=ml.begin(); i!=ml.end(); i++)\r
667                 {\r
668                         tempstr.str("");\r
669                         tempstr << (*i).GetMessageID();\r
670 \r
671                         SendBufferedLine(tempstr.str());\r
672                 }\r
673 \r
674                 // end of multi-line response\r
675                 SendBufferedLine(".");\r
676 \r
677         }\r
678 \r
679         return true;\r
680 }\r
681 \r
682 const bool NNTPConnection::HandleModeCommand(const NNTPCommand &command)\r
683 {\r
684         if(command.m_arguments.size()>0)\r
685         {\r
686                 std::string arg=command.m_arguments[0];\r
687                 StringFunctions::UpperCase(arg,arg);\r
688                 if(arg=="READER")\r
689                 {\r
690                         m_status.m_mode=MODE_READER;\r
691                         if(m_status.m_allowpost==true)\r
692                         {\r
693                                 SendBufferedLine("200 Posting allowed");\r
694                         }\r
695                         else\r
696                         {\r
697                                 SendBufferedLine("201 Posting prohibited");\r
698                         }\r
699                         \r
700                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand set mode to reader");\r
701                 }\r
702                 else\r
703                 {\r
704                         SendBufferedLine("501 Syntax error");\r
705                         m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand unknown MODE argument : "+arg);\r
706                 }\r
707         }\r
708         else\r
709         {\r
710                 SendBufferedLine("501 Syntax error");\r
711                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleModeCommand no argument supplied for MODE");     \r
712         }\r
713 \r
714         return true;\r
715 }\r
716 \r
717 const bool NNTPConnection::HandleNewGroupsCommand(const NNTPCommand &command)\r
718 {\r
719         if(command.m_arguments.size()>=2)\r
720         {\r
721                 DateTime date;\r
722                 int tempint;\r
723                 if(command.m_arguments[0].size()==8)\r
724                 {\r
725                         StringFunctions::Convert(command.m_arguments[0].substr(0,4),tempint);\r
726                         date.SetYear(tempint);\r
727                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
728                         date.SetMonth(tempint);\r
729                         StringFunctions::Convert(command.m_arguments[0].substr(6,2),tempint);\r
730                         date.SetDay(tempint);\r
731                 }\r
732                 else\r
733                 {\r
734                         /*\r
735                         from RFC 3977\r
736                         If the first two digits of the year are not specified\r
737                         (this is supported only for backward compatibility), the year is to\r
738                         be taken from the current century if yy is smaller than or equal to\r
739                         the current year, and the previous century otherwise.\r
740                         */\r
741                         int century;\r
742                         DateTime now;\r
743                         now.SetToGMTime();\r
744                         century=now.GetYear()-(now.GetYear()%100);\r
745 \r
746                         StringFunctions::Convert(command.m_arguments[0].substr(0,2),tempint);\r
747                         tempint<=now.GetYear()-century ? tempint+=century : tempint+=(century-100);\r
748                         \r
749                         //tempint > 50 ? tempint+=1900 : tempint+=2000;\r
750                         \r
751                         date.SetYear(tempint);\r
752                         StringFunctions::Convert(command.m_arguments[0].substr(2,2),tempint);\r
753                         date.SetMonth(tempint);\r
754                         StringFunctions::Convert(command.m_arguments[0].substr(4,2),tempint);\r
755                         date.SetDay(tempint);\r
756                 }\r
757 \r
758                 date.Normalize();\r
759 \r
760                 BoardList bl;\r
761 \r
762                 bl.LoadNew(date.Format("%Y-%m-%d %H:%M:%S"));\r
763 \r
764                 SendBufferedLine("231 List of new newsgroups follows");\r
765 \r
766                 for(BoardList::iterator i=bl.begin(); i!=bl.end(); i++)\r
767                 {\r
768                         if((*i).GetSaveReceivedMessages()==true)\r
769                         {\r
770                                 std::ostringstream tempstr;\r
771                                 tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << m_status.m_allowpost ? "y" : "n";\r
772                                 SendBufferedLine(tempstr.str());\r
773                         }\r
774                 }\r
775 \r
776                 SendBufferedLine(".");\r
777 \r
778         }\r
779         else\r
780         {\r
781                 SendBufferedLine("501 Syntax error");\r
782                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleNewGroupsCommand syntax error");\r
783         }\r
784 \r
785         return true;\r
786 \r
787 }\r
788 \r
789 const bool NNTPConnection::HandleNextCommand(const NNTPCommand &command)\r
790 {\r
791         if(m_status.m_boardid!=-1)\r
792         {\r
793                 if(m_status.m_messageid!=-1)\r
794                 {\r
795                         Message mess;\r
796 \r
797                         if(mess.LoadNext(m_status.m_messageid,m_status.m_boardid))\r
798                         {\r
799                                 std::ostringstream tempstr;\r
800 \r
801                                 m_status.m_messageid=mess.GetMessageID();\r
802 \r
803                                 tempstr << "223 " << mess.GetMessageID() << " " << mess.GetNNTPArticleID();\r
804 \r
805                                 SendBufferedLine(tempstr.str());\r
806 \r
807                         }\r
808                         else\r
809                         {\r
810                                 SendBufferedLine("421 No next article in this group");\r
811                         }\r
812                 }\r
813                 else\r
814                 {\r
815                         SendBufferedLine("420 Current article number is invalid");\r
816                 }\r
817         }\r
818         else\r
819         {\r
820                 SendBufferedLine("412 No newsgroup selected");\r
821         }\r
822 \r
823         return true;\r
824 \r
825 }\r
826 \r
827 const bool NNTPConnection::HandleOverCommand(const NNTPCommand &command)\r
828 {\r
829         long lowmessageid,highmessageid;\r
830         std::string messageuuid="";\r
831 \r
832         lowmessageid=highmessageid=-2;\r
833 \r
834         if(command.m_arguments.size()==0)\r
835         {\r
836                 lowmessageid=m_status.m_messageid;\r
837                 highmessageid=m_status.m_messageid;\r
838         }\r
839         else\r
840         {\r
841                 // Message-ID\r
842                 if(command.m_arguments.size()>0 && command.m_arguments[0].find("<")==0 && command.m_arguments[0].find(">")>0)\r
843                 {\r
844                         messageuuid=command.m_arguments[0];\r
845                         messageuuid=StringFunctions::Replace(messageuuid,"<","");\r
846                         messageuuid=StringFunctions::Replace(messageuuid,">","");\r
847                         /*\r
848                         // get rid of @ and everything after\r
849                         if(messageuuid.find("@")!=std::string::npos)\r
850                         {\r
851                                 messageuuid.erase(messageuuid.find("@"));\r
852                         }\r
853                         */\r
854                 }\r
855                 // single article or range\r
856                 else\r
857                 {\r
858                         // range\r
859                         if(command.m_arguments[0].find("-")!=std::string::npos)\r
860                         {\r
861                                 std::vector<std::string> rangeparts;\r
862                                 StringFunctions::Split(command.m_arguments[0],"-",rangeparts);\r
863                                 // no upper bound\r
864                                 if(rangeparts.size()>0)\r
865                                 {\r
866                                         StringFunctions::Convert(rangeparts[0],lowmessageid);\r
867                                         highmessageid=-1;\r
868                                 }\r
869                                 //upper bound\r
870                                 else if(rangeparts.size()>1)\r
871                                 {\r
872                                         StringFunctions::Convert(rangeparts[1],highmessageid);\r
873                                 }\r
874                         }\r
875                         // single\r
876                         else\r
877                         {\r
878                                 StringFunctions::Convert(command.m_arguments[0],lowmessageid);\r
879                         }\r
880                 }\r
881         }\r
882 \r
883         if(messageuuid!="")\r
884         {\r
885                 Message mess;\r
886                 if(mess.Load(messageuuid))\r
887                 {\r
888                         SendBufferedLine("224 Overview information follows");\r
889                         SendArticleOverInfo(mess);\r
890                         SendBufferedLine(".");\r
891                 }\r
892                 else\r
893                 {\r
894                         SendBufferedLine("423 No such article");\r
895                 }\r
896         }\r
897         else\r
898         {\r
899                 Board bd;\r
900                 if(m_status.m_boardid!=-1 && bd.Load(m_status.m_boardid))\r
901                 {\r
902                         // single message\r
903                         if(highmessageid==-2)\r
904                         {\r
905                                 Message mess;\r
906                                 if(mess.Load(lowmessageid,m_status.m_boardid))\r
907                                 {\r
908                                         SendBufferedLine("224 Overview information follows");\r
909                                         SendArticleOverInfo(mess);\r
910                                         SendBufferedLine(".");\r
911                                 }\r
912                                 else\r
913                                 {\r
914                                         SendBufferedLine("423 No such article in this group");\r
915                                 }\r
916                         }\r
917                         // range with no upper bound\r
918                         else if(highmessageid==-1)\r
919                         {\r
920                                 MessageList ml;\r
921                                 ml.LoadRange(lowmessageid,bd.GetHighMessageID(),m_status.m_boardid);\r
922                                 if(ml.size()>0)\r
923                                 {\r
924                                         SendBufferedLine("224 Overview information follows");\r
925                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
926                                         {\r
927                                                 SendArticleOverInfo((*i));\r
928                                         }\r
929                                         SendBufferedLine(".");\r
930                                 }\r
931                                 else\r
932                                 {\r
933                                         SendBufferedLine("423 Empty range");\r
934                                 }\r
935                         }\r
936                         // range with upper and lower bound\r
937                         else if(highmessageid>=lowmessageid)\r
938                         {\r
939                                 MessageList ml;\r
940                                 ml.LoadRange(lowmessageid,highmessageid,m_status.m_boardid);\r
941                                 if(ml.size()>0)\r
942                                 {\r
943                                         SendBufferedLine("224 Overview information follows");\r
944                                         for(MessageList::iterator i=ml.begin(); i!=ml.end(); i++)\r
945                                         {\r
946                                                 SendArticleOverInfo((*i));\r
947                                         }\r
948                                         SendBufferedLine(".");\r
949                                 }\r
950                                 else\r
951                                 {\r
952                                         SendBufferedLine("423 Empty range");\r
953                                 }\r
954                         }\r
955                         // invalid range\r
956                         else\r
957                         {\r
958                                 SendBufferedLine("423 Empty range");\r
959                         }\r
960                 }\r
961                 else\r
962                 {\r
963                         SendBufferedLine("423 No newsgroup selected");\r
964                 }\r
965         }\r
966 \r
967         return true;\r
968 \r
969 }\r
970 \r
971 const bool NNTPConnection::HandlePostCommand(const NNTPCommand &command)\r
972 {\r
973         if(m_status.m_allowpost==true)\r
974         {\r
975                 SendBufferedLine("340 Send article to be posted");\r
976                 m_status.m_isposting=true;\r
977         }\r
978         else\r
979         {\r
980                 SendBufferedLine("440 Posting not permitted");\r
981         }\r
982 \r
983         return true;\r
984 }\r
985 \r
986 void NNTPConnection::HandlePostedMessage(const std::string &message)\r
987 {\r
988         Message mess;\r
989 \r
990         if(mess.ParseNNTPMessage(message))\r
991         {\r
992                 // if we authenticated, set the username to the authenticated user\r
993                 if(m_status.m_authenticated)\r
994                 {\r
995                         mess.SetFromName(m_status.m_authuser.GetName());\r
996                 }\r
997                 // handle a messages posted to an adminboard\r
998                 if(mess.PostedToAdministrationBoard()==true)\r
999                 {\r
1000                         mess.HandleAdministrationMessage();\r
1001                 }\r
1002                 if(mess.StartFreenetInsert())\r
1003                 {\r
1004                         SendBufferedLine("240 Article received OK");\r
1005                 }\r
1006                 else\r
1007                 {\r
1008                         SendBufferedLine("441 Posting failed.  Make sure the identity you are sending with exists!");\r
1009                 }\r
1010         }\r
1011         else\r
1012         {\r
1013                 SendBufferedLine("441 Posting failed");\r
1014         }\r
1015 }\r
1016 \r
1017 void NNTPConnection::HandleReceivedData()\r
1018 {\r
1019         if(m_status.m_isposting==false)\r
1020         {\r
1021                 // get end of command line\r
1022                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n");\r
1023                 \r
1024                 // we got a command\r
1025                 if(endpos!=m_receivebuffer.end())\r
1026                 {\r
1027                         NNTPCommand command;\r
1028                         std::string commandline(m_receivebuffer.begin(),endpos);\r
1029 \r
1030                         // remove command from receive buffer\r
1031                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+2);\r
1032 \r
1033                         // remove any leading/trailing whitespace\r
1034                         commandline=StringFunctions::TrimWhitespace(commandline);\r
1035 \r
1036                         // split out command and arguments separated by space or tab\r
1037                         StringFunctions::SplitMultiple(commandline," \t",command.m_arguments);\r
1038 \r
1039                         // command is first element in argument vector\r
1040                         command.m_command=command.m_arguments[0];\r
1041                         // erase command from argument vector and make it upper case\r
1042                         command.m_arguments.erase(command.m_arguments.begin());\r
1043                         StringFunctions::UpperCase(command.m_command,command.m_command);\r
1044 \r
1045                         if(HandleCommand(command)==true)\r
1046                         {\r
1047                                 \r
1048                         }\r
1049                         else\r
1050                         {\r
1051                                 SendBufferedLine("500 Unknown command");\r
1052 \r
1053                                 m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPConnection::HandleReceivedData received unhandled NNTP command : "+commandline);\r
1054                         }\r
1055 \r
1056                 }\r
1057 \r
1058         }\r
1059         else\r
1060         {\r
1061                 // check for end of post\r
1062                 std::vector<char>::iterator endpos=Find(m_receivebuffer,"\r\n.\r\n");\r
1063 \r
1064                 if(endpos!=m_receivebuffer.end())\r
1065                 {\r
1066                         // get the message\r
1067                         std::string message(m_receivebuffer.begin(),endpos);\r
1068                         // remove from receive buffer\r
1069                         m_receivebuffer.erase(m_receivebuffer.begin(),endpos+5);\r
1070 \r
1071                         // get rid of dot stuffing ( 2 dots on start of a line - used to prevent premature message end in NNTP)\r
1072                         message=StringFunctions::Replace(message,"\r\n..","\r\n.");\r
1073 \r
1074                         HandlePostedMessage(message);\r
1075 \r
1076                         // message was received, so posting is completed\r
1077                         m_status.m_isposting=false;\r
1078 \r
1079                 }\r
1080         }\r
1081 }\r
1082 \r
1083 const bool NNTPConnection::HandleSetTrustCommand(const NNTPCommand &command)\r
1084 {\r
1085         if(command.m_arguments.size()>=3)\r
1086         {\r
1087                 std::string type=command.m_arguments[0];\r
1088                 StringFunctions::UpperCase(type,type);\r
1089                 if(type=="MESSAGE" || type=="TRUSTLIST")\r
1090                 {\r
1091                         if(m_status.m_authenticated)\r
1092                         {\r
1093                                 bool found=false;\r
1094                                 bool valid=false;\r
1095                                 int trust=-1;\r
1096                                 std::string nntpname="";\r
1097                                 for(int i=1; i<command.m_arguments.size()-1; i++)\r
1098                                 {\r
1099                                         nntpname+=command.m_arguments[i];\r
1100                                 }\r
1101 \r
1102                                 if(command.m_arguments[command.m_arguments.size()-1]!="null")\r
1103                                 {\r
1104                                         StringFunctions::Convert(command.m_arguments[command.m_arguments.size()-1],trust);\r
1105                                 }\r
1106 \r
1107                                 if(trust>=-1 && trust<=100)\r
1108                                 {\r
1109                                         valid=true;\r
1110                                 }\r
1111 \r
1112                                 TrustExtension tr(m_status.m_authuser.GetID());\r
1113 \r
1114                                 if(type=="MESSAGE")\r
1115                                 {\r
1116                                         if(tr.SetMessageTrust(nntpname,trust))\r
1117                                         {\r
1118                                                 found=true;\r
1119                                         }\r
1120                                 }\r
1121                                 if(type=="TRUSTLIST")\r
1122                                 {\r
1123                                         if(tr.SetTrustListTrust(nntpname,trust))\r
1124                                         {\r
1125                                                 found=true;\r
1126                                         }\r
1127                                 }\r
1128 \r
1129                                 if(found && valid)\r
1130                                 {\r
1131                                         SendBufferedLine("280 Trust Set");\r
1132                                 }\r
1133                                 else if(found==false)\r
1134                                 {\r
1135                                         SendBufferedLine("480 Identity not found");\r
1136                                 }\r
1137                                 else\r
1138                                 {\r
1139                                         SendBufferedLine("501 Syntax error");\r
1140                                 }\r
1141 \r
1142                         }\r
1143                         else\r
1144                         {\r
1145                                 SendBufferedLine("480 User not authenticated");\r
1146                         }\r
1147                 }\r
1148                 else\r
1149                 {\r
1150                         SendBufferedLine("501 Syntax error");\r
1151                 }\r
1152         }\r
1153         else\r
1154         {\r
1155                 SendBufferedLine("501 Syntax error");\r
1156         }\r
1157         return true;\r
1158 }\r
1159 \r
1160 const bool NNTPConnection::HandleStatCommand(const NNTPCommand &command)\r
1161 {\r
1162         SendArticleParts(command);\r
1163 \r
1164         return true;\r
1165 }\r
1166 \r
1167 const bool NNTPConnection::HandleQuitCommand(const NNTPCommand &command)\r
1168 {\r
1169         SendBufferedLine("205 Connection Closing");\r
1170         SocketSend();\r
1171         Disconnect();\r
1172         m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::HandleQuitCommand client closed connection");\r
1173         return true;\r
1174 }\r
1175 \r
1176 void NNTPConnection::Run()\r
1177 {\r
1178         struct timeval tv;\r
1179         fd_set writefs,readfs;\r
1180         int rval;\r
1181 \r
1182         // seed random number generater for this thread\r
1183         srand(time(NULL));\r
1184         \r
1185         if(m_status.m_allowpost==true)\r
1186         {\r
1187                 SendBufferedLine("200 Service available, posting allowed");\r
1188         }\r
1189         else\r
1190         {\r
1191                 SendBufferedLine("201 Service available, posting prohibited");\r
1192         }\r
1193 \r
1194         do\r
1195         {\r
1196                 FD_ZERO(&readfs);\r
1197                 FD_ZERO(&writefs);\r
1198                 \r
1199                 FD_SET(m_socket,&readfs);\r
1200                 if(m_sendbuffer.size()>0)\r
1201                 {\r
1202                         FD_SET(m_socket,&writefs);\r
1203                 }\r
1204                 \r
1205                 tv.tv_sec=1;\r
1206                 tv.tv_usec=0;\r
1207                 \r
1208                 rval=select(m_socket+1,&readfs,&writefs,0,&tv);\r
1209                 \r
1210                 if(rval>0)\r
1211                 {\r
1212                         if(FD_ISSET(m_socket,&readfs))\r
1213                         {\r
1214                                 SocketReceive();\r
1215                                 HandleReceivedData();\r
1216                         }\r
1217                         if(m_socket!=INVALID_SOCKET && FD_ISSET(m_socket,&writefs))\r
1218                         {\r
1219                                 SocketSend();\r
1220                         }\r
1221                 }\r
1222                 else if(rval==SOCKET_ERROR)\r
1223                 {\r
1224                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::run select returned -1 : "+GetSocketErrorMessage());   \r
1225                 }\r
1226 \r
1227 //      }while(!Disconnected() && !ZThread::Thread::interrupted());\r
1228         }while(!Disconnected() && !IsCancelled());\r
1229 \r
1230         Disconnect();\r
1231 \r
1232 }\r
1233 \r
1234 void NNTPConnection::SendArticleOverInfo(Message &message)\r
1235 {\r
1236         std::string tempval;\r
1237         std::string line;\r
1238         std::map<long,std::string> references;\r
1239 \r
1240         StringFunctions::Convert(message.GetMessageID(),tempval);\r
1241         line=tempval+"\t";\r
1242         line+=message.GetSubject()+"\t";\r
1243         line+=message.GetFromName()+"\t";\r
1244         line+=message.GetDateTime().Format("%a, %d %b %y %H:%M:%S -0000")+"\t";\r
1245         line+=message.GetNNTPArticleID()+"\t";\r
1246         references=message.GetInReplyTo();\r
1247         if(references.size()>0)\r
1248         {\r
1249                 for(std::map<long,std::string>::reverse_iterator i=references.rbegin(); i!=references.rend(); i++)\r
1250                 {\r
1251                         if(i!=references.rbegin())\r
1252                         {\r
1253                                 line+=" ";\r
1254                         }\r
1255                         line+="<"+(*i).second+">"; //+"@freenetproject.org>";\r
1256                 }\r
1257                 line+="\t";\r
1258         }\r
1259         else\r
1260         {\r
1261                 line+="\t";\r
1262         }\r
1263         line+="\t";\r
1264 \r
1265         SendBufferedLine(line);\r
1266 }\r
1267 \r
1268 void NNTPConnection::SendArticleParts(const NNTPConnection::NNTPCommand &command)\r
1269 {\r
1270         bool sendheaders,sendbody;\r
1271         std::string successcode;\r
1272 \r
1273         if(command.m_command=="ARTICLE")\r
1274         {\r
1275                 sendheaders=true;\r
1276                 sendbody=true;\r
1277                 successcode="220";\r
1278         }\r
1279         else if(command.m_command=="HEAD")\r
1280         {\r
1281                 sendheaders=true;\r
1282                 sendbody=false;\r
1283                 successcode="221";\r
1284         }\r
1285         else if(command.m_command=="BODY")\r
1286         {\r
1287                 sendheaders=false;\r
1288                 sendbody=true;\r
1289                 successcode="222";\r
1290         }\r
1291         else if(command.m_command=="STAT")\r
1292         {\r
1293                 sendheaders=false;\r
1294                 sendbody=false;\r
1295                 successcode="223";\r
1296         }\r
1297 \r
1298         Message message;\r
1299         int messageid=m_status.m_messageid;\r
1300         std::string articleid="";\r
1301         int type=0;     // default to current messageid, 1=messageid, 2=articleid\r
1302 \r
1303         if(command.m_arguments.size()==1 && command.m_arguments[0].size()>0)\r
1304         {\r
1305                 if(command.m_arguments[0].find("<")==std::string::npos)\r
1306                 {\r
1307                         StringFunctions::Convert(command.m_arguments[0],messageid);\r
1308                         message.Load(messageid,m_status.m_boardid);\r
1309                         m_status.m_messageid=message.GetMessageID();\r
1310                         type=1;\r
1311                 }\r
1312                 else\r
1313                 {\r
1314                         articleid=command.m_arguments[0];\r
1315                         //strip off < and > and everthing after @\r
1316                         if(articleid.size()>0 && articleid[0]=='<')\r
1317                         {\r
1318                                 articleid.erase(0,1);\r
1319                         }\r
1320                         if(articleid.size()>0 && articleid[articleid.size()-1]=='>')\r
1321                         {\r
1322                                 articleid.erase(articleid.size()-1);\r
1323                         }\r
1324                         /*\r
1325                         if(articleid.size()>0 && articleid.find('@')!=std::string::npos)\r
1326                         {\r
1327                                 articleid.erase(articleid.find('@'));\r
1328                         }\r
1329                         */\r
1330                         message.Load(articleid);\r
1331                         type=2;\r
1332                 }\r
1333         }\r
1334         else\r
1335         {\r
1336                 message.Load(m_status.m_messageid,m_status.m_boardid);\r
1337         }\r
1338 \r
1339         switch(type)\r
1340         {\r
1341         case 0:\r
1342                 if(m_status.m_boardid!=-1)\r
1343                 {\r
1344                         if(m_status.m_messageid!=-1)\r
1345                         {\r
1346                                 std::ostringstream tempstr;\r
1347                                 std::string article;\r
1348                                 if(sendheaders&&sendbody)\r
1349                                 {\r
1350                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1351                                 }\r
1352                                 else if(sendheaders && !sendbody)\r
1353                                 {\r
1354                                         article=message.GetNNTPHeaders();\r
1355                                         // strip off final \r\n from headers\r
1356                                         if(article.rfind("\r\n")==article.size()-2)\r
1357                                         {\r
1358                                                 article.erase(article.size()-2);\r
1359                                         }\r
1360                                 }\r
1361                                 else\r
1362                                 {\r
1363                                         article=message.GetNNTPBody();\r
1364                                 }\r
1365                                 // dot stuff article\r
1366                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1367 \r
1368                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1369 \r
1370                                 SendBufferedLine(tempstr.str());\r
1371                                 if(sendheaders || sendbody)\r
1372                                 {\r
1373                                         SendBufferedLine(article);\r
1374                                         SendBufferedLine(".");\r
1375                                 }\r
1376 \r
1377                         }\r
1378                         else\r
1379                         {\r
1380                                 SendBufferedLine("420 Current article number is invalid");\r
1381                         }\r
1382                 }\r
1383                 else\r
1384                 {\r
1385                         SendBufferedLine("412 No newsgroup selected");\r
1386                 }\r
1387                 break;\r
1388         case 1:\r
1389                 if(m_status.m_boardid!=-1)\r
1390                 {\r
1391                         if(message.GetMessageID()!=-1)\r
1392                         {\r
1393                                 std::ostringstream tempstr;\r
1394                                 std::string article;\r
1395                                 if(sendheaders&&sendbody)\r
1396                                 {\r
1397                                         article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1398                                 }\r
1399                                 else if(sendheaders && !sendbody)\r
1400                                 {\r
1401                                         article=message.GetNNTPHeaders();\r
1402                                         // strip off final \r\n from headers\r
1403                                         if(article.rfind("\r\n")==article.size()-2)\r
1404                                         {\r
1405                                                 article.erase(article.size()-2);\r
1406                                         }\r
1407                                 }\r
1408                                 else\r
1409                                 {\r
1410                                         article=message.GetNNTPBody();\r
1411                                 }\r
1412                                 // dot stuff article\r
1413                                 article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1414 \r
1415                                 tempstr << successcode << " " << message.GetMessageID() << " " << message.GetNNTPArticleID();\r
1416 \r
1417                                 SendBufferedLine(tempstr.str());\r
1418                                 if(sendheaders || sendbody)\r
1419                                 {\r
1420                                         SendBufferedLine(article);\r
1421                                         SendBufferedLine(".");\r
1422                                 }\r
1423                         }\r
1424                         else\r
1425                         {\r
1426                                 SendBufferedLine("423 No article with that number");\r
1427                         }\r
1428                 }\r
1429                 else\r
1430                 {\r
1431                         SendBufferedLine("412 No newsgroup selected");\r
1432                 }\r
1433                 break;\r
1434         case 2:\r
1435                 if(message.GetMessageID()!=-1)\r
1436                 {\r
1437                         std::string article;\r
1438                         if(sendheaders&&sendbody)\r
1439                         {\r
1440                                 article=message.GetNNTPHeaders()+"\r\n"+message.GetNNTPBody();\r
1441                         }\r
1442                         else if(sendheaders && !sendbody)\r
1443                         {\r
1444                                 article=message.GetNNTPHeaders();\r
1445                                 // strip off final \r\n from headers\r
1446                                 if(article.rfind("\r\n")==article.size()-2)\r
1447                                 {\r
1448                                         article.erase(article.size()-2);\r
1449                                 }\r
1450                         }\r
1451                         else\r
1452                         {\r
1453                                 article=message.GetNNTPBody();\r
1454                         }\r
1455                         // dot stuff article\r
1456                         article=StringFunctions::Replace(article,"\r\n.","\r\n..");\r
1457 \r
1458                         SendBufferedLine(successcode+" 0 "+message.GetNNTPArticleID());\r
1459                         if(sendheaders || sendbody)\r
1460                         {\r
1461                                 SendBufferedLine(article);\r
1462                                 SendBufferedLine(".");\r
1463                         }\r
1464                 }\r
1465                 else\r
1466                 {\r
1467                         SendBufferedLine("430 No article with that message-id");\r
1468                 }\r
1469                 break;\r
1470         }\r
1471 \r
1472 }\r
1473 \r
1474 void NNTPConnection::SendBuffered(const std::string &data)\r
1475 {\r
1476         m_sendbuffer.insert(m_sendbuffer.end(),data.begin(),data.end());\r
1477 }\r
1478 \r
1479 void NNTPConnection::SocketReceive()\r
1480 {\r
1481         int rval=recv(m_socket,&m_tempbuffer[0],m_tempbuffer.size(),0);\r
1482         if(rval>0)\r
1483         {\r
1484                 m_receivebuffer.insert(m_receivebuffer.end(),m_tempbuffer.begin(),m_tempbuffer.begin()+rval);\r
1485         }\r
1486         else if(rval==0)\r
1487         {\r
1488                 Disconnect();\r
1489                 m_log->WriteLog(LogFile::LOGLEVEL_INFO,"NNTPConnection::SocketReceive remote host closed connection");\r
1490         }\r
1491         else if(rval==-1)\r
1492         {\r
1493                 std::string errnostr;\r
1494                 StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1495                 // error on receive - close the connection\r
1496                 Disconnect();\r
1497                 m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketReceive recv returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1498         }\r
1499 }\r
1500 \r
1501 void NNTPConnection::SocketSend()\r
1502 {\r
1503         if(m_sendbuffer.size()>0 && m_socket!=INVALID_SOCKET)\r
1504         {\r
1505                 int rval=send(m_socket,&m_sendbuffer[0],m_sendbuffer.size(),0);\r
1506                 if(rval>0)\r
1507                 {       \r
1508                         m_sendbuffer.erase(m_sendbuffer.begin(),m_sendbuffer.begin()+rval);\r
1509                 }\r
1510                 else if(rval==-1)\r
1511                 {\r
1512                         std::string errnostr;\r
1513                         StringFunctions::Convert(GetSocketErrorNumber(),errnostr);\r
1514                         m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"NNTPConnection::SocketSend returned -1 : "+errnostr+" - "+GetSocketErrorMessage());\r
1515                 }\r
1516         }\r
1517 }\r