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