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