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