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