inetprot.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:28k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * inetprot.cxx
  3.  *
  4.  * Internet Protocol ancestor class.
  5.  *
  6.  * Portable Windows Library
  7.  *
  8.  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
  25.  * All Rights Reserved.
  26.  *
  27.  * Contributor(s): ______________________________________.
  28.  *
  29.  * $Log: inetprot.cxx,v $
  30.  * Revision 1.45  2000/05/05 10:08:29  robertj
  31.  * Fixed some GNU compiler warnings
  32.  *
  33.  * Revision 1.44  2000/05/02 08:29:07  craigs
  34.  * Removed "memory leaks" caused by brain-dead GNU linker
  35.  *
  36.  * Revision 1.43  1999/05/04 15:26:01  robertj
  37.  * Improved HTTP/1.1 compatibility (pass through user commands).
  38.  * Fixed problems with quicktime installer.
  39.  *
  40.  * Revision 1.42  1998/12/04 10:08:01  robertj
  41.  * Fixed bug in PMIMInfo read functions, should clear entries before loading.
  42.  *
  43.  * Revision 1.41  1998/11/30 04:52:02  robertj
  44.  * New directory structure
  45.  *
  46.  * Revision 1.40  1998/11/03 01:03:09  robertj
  47.  * Fixed problem with multiline response that is non-numeric.
  48.  *
  49.  * Revision 1.39  1998/10/16 02:05:55  robertj
  50.  * Tried to make ReadLine more forgiving of CR CR LF combination.
  51.  *
  52.  * Revision 1.38  1998/09/23 06:22:20  robertj
  53.  * Added open source copyright license.
  54.  *
  55.  * Revision 1.37  1998/07/24 06:55:00  robertj
  56.  * Improved robustness of base64 decoding.
  57.  *
  58.  * Revision 1.36  1998/02/03 06:20:25  robertj
  59.  * Fixed bug in Accept() function passing on to IP Accept().
  60.  *
  61.  * Revision 1.35  1998/01/26 02:49:20  robertj
  62.  * GNU support.
  63.  *
  64.  * Revision 1.34  1998/01/26 00:46:48  robertj
  65.  * Fixed Connect functions on PInternetProtocol so propagates read timeout variable so can adjust the connect timeout..
  66.  *
  67.  * Revision 1.33  1997/11/06 10:26:48  robertj
  68.  * Fixed bug in debug dump of MIME dictionary, did not have linefeeds between entries.
  69.  *
  70.  * Revision 1.32  1997/06/09 04:30:03  robertj
  71.  * Fixed multiple MIME field bug.
  72.  *
  73.  * Revision 1.31  1997/06/06 08:53:51  robertj
  74.  * Fixed bug with multiple cookies (MIME fields) are sent to IE.
  75.  *
  76.  * Revision 1.30  1997/03/28 13:04:37  robertj
  77.  * Fixed bug for multiple fields in MIME headers, especially cookies.
  78.  *
  79.  * Revision 1.29  1997/03/18 21:26:46  robertj
  80.  * Fixed stream write of MIME putting double CR's in text files..
  81.  *
  82.  * Revision 1.28  1997/02/05 11:53:13  robertj
  83.  * Changed construction of MIME dictionary to be delayed untill it is used.
  84.  *
  85.  * Revision 1.27  1996/12/05 11:41:12  craigs
  86.  * Fix problem with STAT command response containing lines not starting
  87.  * with response number
  88.  *
  89.  * Revision 1.26  1996/10/08 13:07:39  robertj
  90.  * Changed default for assert to be ignore, not abort.
  91.  *
  92.  * Revision 1.25  1996/09/16 12:57:07  robertj
  93.  * Fixed missing propagationof errors on open of subchannel.
  94.  *
  95.  * Revision 1.24  1996/09/14 13:09:36  robertj
  96.  * Major upgrade:
  97.  *   rearranged sockets to help support IPX.
  98.  *   added indirect channel class and moved all protocols to descend from it,
  99.  *   separating the protocol from the low level byte transport.
  100.  *
  101.  * Revision 1.23  1996/08/25 09:35:47  robertj
  102.  * Added bug in appsock that last response is set on an I/O error.
  103.  *
  104.  * Revision 1.22  1996/07/15 10:33:14  robertj
  105.  * Changed memory block base64 conversion functions to be void *.
  106.  *
  107.  * Revision 1.21  1996/06/03 11:58:43  robertj
  108.  * Fixed bug in reading successive UnRead() calls getting save in wrong order.
  109.  *
  110.  * Revision 1.20  1996/05/26 03:46:22  robertj
  111.  * Compatibility to GNU 2.7.x
  112.  *
  113.  * Revision 1.19  1996/05/15 10:15:15  robertj
  114.  * Added access function to set intercharacter line read timeout.
  115.  *
  116.  * Revision 1.18  1996/05/09 12:14:04  robertj
  117.  * Rewrote the "unread" buffer usage and then used it to improve ReadLine() performance.
  118.  *
  119.  * Revision 1.17  1996/03/31 08:57:34  robertj
  120.  * Changed MIME type for no extension from binary to text.
  121.  * Added flush of data before sending a command.
  122.  * Added version of WriteCommand() and ExecteCommand() without argument string.
  123.  *
  124.  * Revision 1.15  1996/03/18 13:33:13  robertj
  125.  * Fixed incompatibilities to GNU compiler where PINDEX != int.
  126.  *
  127.  * Revision 1.14  1996/03/16 04:53:07  robertj
  128.  * Changed all the get host name and get host address functions to be more consistent.
  129.  * Added ParseReponse() for splitting reponse line into code and info.
  130.  * Changed lastResponseCode to an integer.
  131.  * Fixed bug in MIME write function, should be const.
  132.  * Added PString parameter version of UnRead().
  133.  *
  134.  * Revision 1.13  1996/03/04 12:20:41  robertj
  135.  * Split file into mailsock.cxx
  136.  *
  137.  * Revision 1.12  1996/02/25 11:16:07  robertj
  138.  * Fixed bug in ReadResponse() for multi-line responses under FTP..
  139.  *
  140.  * Revision 1.11  1996/02/25 03:05:12  robertj
  141.  * Added decoding of Base64 to a block of memory instead of PBYTEArray.
  142.  *
  143.  * Revision 1.10  1996/02/19 13:31:26  robertj
  144.  * Changed stuff to use new & operator..
  145.  *
  146.  * Revision 1.9  1996/02/15 14:42:41  robertj
  147.  * Fixed warning for long to int conversion.
  148.  *
  149.  * Revision 1.8  1996/02/13 12:57:49  robertj
  150.  * Added access to the last response in an application socket.
  151.  *
  152.  * Revision 1.7  1996/02/03 11:33:17  robertj
  153.  * Changed RadCmd() so can distinguish between I/O error and unknown command.
  154.  *
  155.  * Revision 1.6  1996/01/28 14:11:11  robertj
  156.  * Fixed bug in MIME content types for non caseless strings.
  157.  * Added default value in string for service name.
  158.  *
  159.  * Revision 1.5  1996/01/28 02:48:27  robertj
  160.  * Removal of MemoryPointer classes as usage didn't work for GNU.
  161.  *
  162.  * Revision 1.4  1996/01/26 02:24:29  robertj
  163.  * Further implemetation.
  164.  *
  165.  * Revision 1.3  1996/01/23 13:18:43  robertj
  166.  * Major rewrite for HTTP support.
  167.  *
  168.  * Revision 1.2  1995/11/09 12:19:29  robertj
  169.  * Fixed missing state assertion in state machine.
  170.  *
  171.  * Revision 1.1  1995/06/17 00:50:37  robertj
  172.  * Initial revision
  173.  *
  174.  */
  175. #ifdef __GNUC__
  176. #pragma implementation "inetprot.h"
  177. #pragma implementation "mime.h"
  178. #endif
  179. #include <ptlib.h>
  180. #include <ptlib/sockets.h>
  181. #include <ptclib/inetprot.h>
  182. #include <ptclib/mime.h>
  183. static const char * CRLF = "rn";
  184. //////////////////////////////////////////////////////////////////////////////
  185. // PInternetProtocol
  186. PInternetProtocol::PInternetProtocol(const char * svcName,
  187.                                      PINDEX cmdCount,
  188.                                      char const * const * cmdNames)
  189.   : defaultServiceName(svcName),
  190.     commandNames(cmdCount, cmdNames, TRUE),
  191.     readLineTimeout(0, 10)   // 10 seconds
  192. {
  193.   SetReadTimeout(PTimeInterval(0, 0, 10));  // 10 minutes
  194.   stuffingState = DontStuff;
  195.   newLineToCRLF = TRUE;
  196.   unReadCount = 0;
  197. }
  198. void PInternetProtocol::SetReadLineTimeout(const PTimeInterval & t)
  199. {
  200.   readLineTimeout = t;
  201. }
  202. BOOL PInternetProtocol::Read(void * buf, PINDEX len)
  203. {
  204.   lastReadCount = PMIN(unReadCount, len);
  205.   const char * unReadPtr = ((const char *)unReadBuffer)+unReadCount;
  206.   char * bufptr = (char *)buf;
  207.   while (unReadCount > 0 && len > 0) {
  208.     *bufptr++ = *--unReadPtr;
  209.     unReadCount--;
  210.     len--;
  211.   }
  212.   if (unReadCount == 0)
  213.     unReadBuffer.SetSize(0);
  214.   if (len > 0) {
  215.     PINDEX saveCount = lastReadCount;
  216.     PIndirectChannel::Read(bufptr, len);
  217.     lastReadCount += saveCount;
  218.   }
  219.   return lastReadCount > 0;
  220. }
  221. BOOL PInternetProtocol::Write(const void * buf, PINDEX len)
  222. {
  223.   if (len == 0 || stuffingState == DontStuff)
  224.     return PIndirectChannel::Write(buf, len);
  225.   PINDEX totalWritten = 0;
  226.   const char * base = (const char *)buf;
  227.   const char * current = base;
  228.   while (len-- > 0) {
  229.     switch (stuffingState) {
  230.       case StuffIdle :
  231.         switch (*current) {
  232.           case 'r' :
  233.             stuffingState = StuffCR;
  234.             break;
  235.           case 'n' :
  236.             if (newLineToCRLF) {
  237.               if (current > base) {
  238.                 if (!PIndirectChannel::Write(base, current - base))
  239.                   return FALSE;
  240.                 totalWritten += lastWriteCount;
  241.               }
  242.               if (!PIndirectChannel::Write("r", 1))
  243.                 return FALSE;
  244.               totalWritten += lastWriteCount;
  245.               base = current;
  246.             }
  247.         }
  248.         break;
  249.       case StuffCR :
  250.         stuffingState = *current != 'n' ? StuffIdle : StuffCRLF;
  251.         break;
  252.       case StuffCRLF :
  253.         if (*current == '.') {
  254.           if (current > base) {
  255.             if (!PIndirectChannel::Write(base, current - base))
  256.               return FALSE;
  257.             totalWritten += lastWriteCount;
  258.           }
  259.           if (!PIndirectChannel::Write(".", 1))
  260.             return FALSE;
  261.           totalWritten += lastWriteCount;
  262.           base = current;
  263.         }
  264.         // Then do default state
  265.       default :
  266.         stuffingState = StuffIdle;
  267.         break;
  268.     }
  269.     current++;
  270.   }
  271.   if (current > base)
  272.     if (!PIndirectChannel::Write(base, current - base))
  273.       return FALSE;
  274.   lastWriteCount += totalWritten;
  275.   return lastWriteCount > 0;
  276. }
  277. BOOL PInternetProtocol::AttachSocket(PIPSocket * socket)
  278. {
  279.   if (socket->IsOpen()) {
  280.     if (Open(socket))
  281.       return TRUE;
  282.     Close();
  283.     lastError = Miscellaneous;
  284.     osError = 0x41000000;
  285.   }
  286.   else {
  287.     lastError = socket->GetErrorCode();
  288.     osError = socket->GetErrorNumber();
  289.     delete socket;
  290.   }
  291.   return FALSE;
  292. }
  293. BOOL PInternetProtocol::Connect(const PString & address, WORD port)
  294. {
  295.   if (port == 0)
  296.     return Connect(address, defaultServiceName);
  297.   if (readTimeout == PMaxTimeInterval)
  298.     return AttachSocket(new PTCPSocket(address, port));
  299.   PTCPSocket * s = new PTCPSocket(port);
  300.   s->SetReadTimeout(readTimeout);
  301.   s->Connect(address);
  302.   return AttachSocket(s);
  303. }
  304. BOOL PInternetProtocol::Connect(const PString & address, const PString & service)
  305. {
  306.   if (readTimeout == PMaxTimeInterval)
  307.     return AttachSocket(new PTCPSocket(address, service));
  308.   PTCPSocket * s = new PTCPSocket;
  309.   s->SetReadTimeout(readTimeout);
  310.   s->SetPort(service);
  311.   s->Connect(address);
  312.   return AttachSocket(s);
  313. }
  314. BOOL PInternetProtocol::Accept(PSocket & listener)
  315. {
  316.   if (readTimeout == PMaxTimeInterval)
  317.     return AttachSocket(new PTCPSocket(listener));
  318.   PTCPSocket * s = new PTCPSocket;
  319.   s->SetReadTimeout(readTimeout);
  320.   s->Accept(listener);
  321.   return AttachSocket(s);
  322. }
  323. const PString & PInternetProtocol::GetDefaultService() const
  324. {
  325.   return defaultServiceName;
  326. }
  327. PIPSocket * PInternetProtocol::GetSocket() const
  328. {
  329.   PChannel * channel = GetBaseReadChannel();
  330.   if (channel != NULL && channel->IsDescendant(PIPSocket::Class()))
  331.     return (PIPSocket *)channel;
  332.   return NULL;
  333. }
  334. BOOL PInternetProtocol::WriteLine(const PString & line)
  335. {
  336.   if (line.FindOneOf(CRLF) == P_MAX_INDEX)
  337.     return WriteString(line + CRLF);
  338.   PStringArray lines = line.Lines();
  339.   for (PINDEX i = 0; i < lines.GetSize(); i++)
  340.     if (!WriteString(lines[i] + CRLF))
  341.       return FALSE;
  342.   return TRUE;
  343. }
  344. BOOL PInternetProtocol::ReadLine(PString & str, BOOL allowContinuation)
  345. {
  346.   str = PString();
  347.   PCharArray line(100);
  348.   PINDEX count = 0;
  349.   BOOL gotEndOfLine = FALSE;
  350.   int c = ReadChar();
  351.   if (c < 0)
  352.     return FALSE;
  353.   PTimeInterval oldTimeout = GetReadTimeout();
  354.   SetReadTimeout(readLineTimeout);
  355.   while (c >= 0 && !gotEndOfLine) {
  356.     if (unReadCount == 0) {
  357.       char readAhead[1000];
  358.       SetReadTimeout(0);
  359.       if (PIndirectChannel::Read(readAhead, sizeof(readAhead)))
  360.         UnRead(readAhead, GetLastReadCount());
  361.       SetReadTimeout(readLineTimeout);
  362.     }
  363.     switch (c) {
  364.       case 'b' :
  365.       case '177' :
  366.         if (count > 0)
  367.           count--;
  368.         c = ReadChar();
  369.         break;
  370.       case 'r' :
  371.         c = ReadChar();
  372.         switch (c) {
  373.           case -1 :
  374.           case 'n' :
  375.             break;
  376.           case 'r' :
  377.             c = ReadChar();
  378.             if (c == 'n')
  379.               break;
  380.             UnRead(c);
  381.             c = 'r';
  382.             // Then do default case
  383.           default :
  384.             UnRead(c);
  385.         }
  386.         // Then do line feed case
  387.       case 'n' :
  388.         if (count == 0 || !allowContinuation || (c = ReadChar()) < 0)
  389.           gotEndOfLine = TRUE;
  390.         else if (c != ' ' && c != 't') {
  391.           UnRead(c);
  392.           gotEndOfLine = TRUE;
  393.         }
  394.         break;
  395.       default :
  396.         if (count >= line.GetSize())
  397.           line.SetSize(count + 100);
  398.         line[count++] = (char)c;
  399.         c = ReadChar();
  400.     }
  401.   }
  402.   SetReadTimeout(oldTimeout);
  403.   if (count > 0)
  404.     str = PString(line, count);
  405.   return gotEndOfLine;
  406. }
  407. void PInternetProtocol::UnRead(int ch)
  408. {
  409.   unReadBuffer.SetSize((unReadCount+256)&~255);
  410.   unReadBuffer[unReadCount++] = (char)ch;
  411. }
  412. void PInternetProtocol::UnRead(const PString & str)
  413. {
  414.   UnRead((const char *)str, str.GetLength());
  415. }
  416. void PInternetProtocol::UnRead(const void * buffer, PINDEX len)
  417. {
  418.   char * unreadptr =
  419.                unReadBuffer.GetPointer((unReadCount+len+255)&~255)+unReadCount;
  420.   const char * bufptr = ((const char *)buffer)+len;
  421.   unReadCount += len;
  422.   while (len-- > 0)
  423.     *unreadptr++ = *--bufptr;
  424. }
  425. BOOL PInternetProtocol::WriteCommand(PINDEX cmdNumber)
  426. {
  427.   if (cmdNumber >= commandNames.GetSize())
  428.     return FALSE;
  429.   return WriteLine(commandNames[cmdNumber]);
  430. }
  431. BOOL PInternetProtocol::WriteCommand(PINDEX cmdNumber, const PString & param)
  432. {
  433.   if (cmdNumber >= commandNames.GetSize())
  434.     return FALSE;
  435.   if (param.IsEmpty())
  436.     return WriteLine(commandNames[cmdNumber]);
  437.   else
  438.     return WriteLine(commandNames[cmdNumber] & param);
  439. }
  440. BOOL PInternetProtocol::ReadCommand(PINDEX & num, PString & args)
  441. {
  442.   do {
  443.     if (!ReadLine(args))
  444.       return FALSE;
  445.   } while (args.IsEmpty());
  446.   PINDEX endCommand = args.Find(' ');
  447.   if (endCommand == P_MAX_INDEX)
  448.     endCommand = args.GetLength();
  449.   PCaselessString cmd = args.Left(endCommand);
  450.   num = commandNames.GetValuesIndex(cmd);
  451.   if (num != P_MAX_INDEX)
  452.     args = args.Mid(endCommand+1);
  453.   return TRUE;
  454. }
  455. BOOL PInternetProtocol::WriteResponse(unsigned code, const PString & info)
  456. {
  457.   return WriteResponse(psprintf("%03u", code), info);
  458. }
  459. BOOL PInternetProtocol::WriteResponse(const PString & code,
  460.                                        const PString & info)
  461. {
  462.   if (info.FindOneOf(CRLF) == P_MAX_INDEX)
  463.     return WriteString(code & info + CRLF);
  464.   PStringArray lines = info.Lines();
  465.   PINDEX i;
  466.   for (i = 0; i < lines.GetSize()-1; i++)
  467.     if (!WriteString(code + '-' + lines[i] + CRLF))
  468.       return FALSE;
  469.   return WriteString(code & lines[i] + CRLF);
  470. }
  471. BOOL PInternetProtocol::ReadResponse()
  472. {
  473.   PString line;
  474.   if (!ReadLine(line)) {
  475.     lastResponseCode = -1;
  476.     lastResponseInfo = GetErrorText();
  477.     return FALSE;
  478.   }
  479.   PINDEX continuePos = ParseResponse(line);
  480.   if (continuePos == 0)
  481.     return TRUE;
  482.   PString prefix = line.Left(continuePos);
  483.   char continueChar = line[continuePos];
  484.   while (line[continuePos] == continueChar ||
  485.          (!isdigit(line[0]) && strncmp(line, prefix, continuePos) != 0)) {
  486.     lastResponseInfo += 'n';
  487.     if (!ReadLine(line)) {
  488.       lastResponseInfo += GetErrorText();
  489.       return FALSE;
  490.     }
  491.     if (line.Left(continuePos) == prefix)
  492.       lastResponseInfo += line.Mid(continuePos+1);
  493.     else
  494.       lastResponseInfo += line;
  495.   }
  496.   return TRUE;
  497. }
  498. BOOL PInternetProtocol::ReadResponse(int & code, PString & info)
  499. {
  500.   BOOL retval = ReadResponse();
  501.   code = lastResponseCode;
  502.   info = lastResponseInfo;
  503.   return retval;
  504. }
  505. PINDEX PInternetProtocol::ParseResponse(const PString & line)
  506. {
  507.   PINDEX endCode = line.FindOneOf(" -");
  508.   if (endCode == P_MAX_INDEX) {
  509.     lastResponseCode = -1;
  510.     lastResponseInfo = line;
  511.     return 0;
  512.   }
  513.   lastResponseCode = line.Left(endCode).AsInteger();
  514.   lastResponseInfo = line.Mid(endCode+1);
  515.   return line[endCode] != ' ' ? endCode : 0;
  516. }
  517. int PInternetProtocol::ExecuteCommand(PINDEX cmd)
  518. {
  519.   return ExecuteCommand(cmd, PString());
  520. }
  521. int PInternetProtocol::ExecuteCommand(PINDEX cmd,
  522.                                        const PString & param)
  523. {
  524.   PTimeInterval oldTimeout = GetReadTimeout();
  525.   SetReadTimeout(0);
  526.   while (ReadChar() >= 0)
  527.     ;
  528.   SetReadTimeout(oldTimeout);
  529.   return WriteCommand(cmd, param) && ReadResponse() ? lastResponseCode : -1;
  530. }
  531. int PInternetProtocol::GetLastResponseCode() const
  532. {
  533.   return lastResponseCode;
  534. }
  535. PString PInternetProtocol::GetLastResponseInfo() const
  536. {
  537.   return lastResponseInfo;
  538. }
  539. //////////////////////////////////////////////////////////////////////////////
  540. // PMIMEInfo
  541. PMIMEInfo::PMIMEInfo(istream & strm)
  542. {
  543.   ReadFrom(strm);
  544. }
  545. PMIMEInfo::PMIMEInfo(PInternetProtocol & socket)
  546. {
  547.   Read(socket);
  548. }
  549. void PMIMEInfo::PrintOn(ostream &strm) const
  550. {
  551.   BOOL output_cr = strm.fill() == 'r';
  552.   strm.fill(' ');
  553.   for (PINDEX i = 0; i < GetSize(); i++) {
  554.     PString name = GetKeyAt(i) + ": ";
  555.     PString value = GetDataAt(i);
  556.     if (value.FindOneOf("rn") != P_MAX_INDEX) {
  557.       PStringArray vals = value.Lines();
  558.       for (PINDEX j = 0; j < vals.GetSize(); j++) {
  559.         strm << name << vals[j];
  560.         if (output_cr)
  561.           strm << 'r';
  562.         strm << 'n';
  563.       }
  564.     }
  565.     else {
  566.       strm << name << value;
  567.       if (output_cr)
  568.         strm << 'r';
  569.       strm << 'n';
  570.     }
  571.   }
  572.   if (output_cr)
  573.     strm << 'r';
  574.   strm << endl;
  575. }
  576. void PMIMEInfo::ReadFrom(istream &strm)
  577. {
  578.   RemoveAll();
  579.   PString line;
  580.   while (strm.good()) {
  581.     strm >> line;
  582.     if (line.IsEmpty())
  583.       break;
  584.     PINDEX colonPos = line.Find(':');
  585.     if (colonPos != P_MAX_INDEX) {
  586.       PCaselessString fieldName  = line.Left(colonPos).Trim();
  587.       PString fieldValue = line(colonPos+1, P_MAX_INDEX).Trim();
  588.       if (Contains(fieldName))
  589.         fieldValue = (*this)[fieldName] + "n" + fieldValue;
  590.       SetAt(fieldName, fieldValue);
  591.     }
  592.   }
  593. }
  594. BOOL PMIMEInfo::Read(PInternetProtocol & socket)
  595. {
  596.   RemoveAll();
  597.   PString line;
  598.   while (socket.ReadLine(line, TRUE)) {
  599.     if (line.IsEmpty())
  600.       return TRUE;
  601.     PINDEX colonPos = line.Find(':');
  602.     if (colonPos != P_MAX_INDEX) {
  603.       PCaselessString fieldName  = line.Left(colonPos).Trim();
  604.       PString fieldValue = line(colonPos+1, P_MAX_INDEX).Trim();
  605.       if (Contains(fieldName))
  606.         fieldValue = (*this)[fieldName] + "n" + fieldValue;
  607.       SetAt(fieldName, fieldValue);
  608.     }
  609.   }
  610.   return FALSE;
  611. }
  612. BOOL PMIMEInfo::Write(PInternetProtocol & socket) const
  613. {
  614.   for (PINDEX i = 0; i < GetSize(); i++) {
  615.     PString name = GetKeyAt(i) + ": ";
  616.     PString value = GetDataAt(i);
  617.     if (value.FindOneOf("rn") != P_MAX_INDEX) {
  618.       PStringArray vals = value.Lines();
  619.       for (PINDEX j = 0; j < vals.GetSize(); j++) {
  620.         if (!socket.WriteLine(name + vals[j]))
  621.           return FALSE;
  622.       }
  623.     }
  624.     else {
  625.       if (!socket.WriteLine(name + value))
  626.         return FALSE;
  627.     }
  628.   }
  629.   return socket.WriteString(CRLF);
  630. }
  631. PString PMIMEInfo::GetString(const PString & key, const PString & dflt) const
  632. {
  633.   if (GetAt(PCaselessString(key)) == NULL)
  634.     return dflt;
  635.   return operator[](key);
  636. }
  637. long PMIMEInfo::GetInteger(const PString & key, long dflt) const
  638. {
  639.   if (GetAt(PCaselessString(key)) == NULL)
  640.     return dflt;
  641.   return operator[](key).AsInteger();
  642. }
  643. static const PStringToString::Initialiser DefaultContentTypes[] = {
  644.   { ".txt", "text/plain" },
  645.   { ".text", "text/plain" },
  646.   { ".html", "text/html" },
  647.   { ".htm", "text/html" },
  648.   { ".aif", "audio/aiff" },
  649.   { ".aiff", "audio/aiff" },
  650.   { ".au", "audio/basic" },
  651.   { ".snd", "audio/basic" },
  652.   { ".wav", "audio/wav" },
  653.   { ".gif", "image/gif" },
  654.   { ".xbm", "image/x-bitmap" },
  655.   { ".tif", "image/tiff" },
  656.   { ".tiff", "image/tiff" },
  657.   { ".jpg", "image/jpeg" },
  658.   { ".jpe", "image/jpeg" },
  659.   { ".jpeg", "image/jpeg" },
  660.   { ".avi", "video/avi" },
  661.   { ".mpg", "video/mpeg" },
  662.   { ".mpeg", "video/mpeg" },
  663.   { ".qt", "video/quicktime" },
  664.   { ".mov", "video/quicktime" }
  665. };
  666. PStringToString & PMIMEInfo::GetContentTypes()
  667. {
  668.   static PStringToString contentTypes(PARRAYSIZE(DefaultContentTypes),
  669.                                       DefaultContentTypes,
  670.                                       TRUE);
  671.   return contentTypes;
  672. }
  673. void PMIMEInfo::SetAssociation(const PStringToString & allTypes, BOOL merge)
  674. {
  675.   PStringToString & types = GetContentTypes();
  676.   if (!merge)
  677.     types.RemoveAll();
  678.   for (PINDEX i = 0; i < allTypes.GetSize(); i++)
  679.     types.SetAt(allTypes.GetKeyAt(i), allTypes.GetDataAt(i));
  680. }
  681. PString PMIMEInfo::GetContentType(const PString & fType)
  682. {
  683.   if (fType.IsEmpty())
  684.     return "text/plain";
  685.   PStringToString & types = GetContentTypes();
  686.   if (types.Contains(fType))
  687.     return types[fType];
  688.   return "application/octet-stream";
  689. }
  690. ///////////////////////////////////////////////////////////////////////////////
  691. // PBase64
  692. PBase64::PBase64()
  693. {
  694.   StartEncoding();
  695.   StartDecoding();
  696. }
  697. void PBase64::StartEncoding(BOOL useCRLF)
  698. {
  699.   encodedString = "";
  700.   encodeLength = nextLine = saveCount = 0;
  701.   useCRLFs = useCRLF;
  702. }
  703. void PBase64::ProcessEncoding(const PString & str)
  704. {
  705.   ProcessEncoding((const char *)str);
  706. }
  707. void PBase64::ProcessEncoding(const char * cstr)
  708. {
  709.   ProcessEncoding((const BYTE *)cstr, strlen(cstr));
  710. }
  711. void PBase64::ProcessEncoding(const PBYTEArray & data)
  712. {
  713.   ProcessEncoding(data, data.GetSize());
  714. }
  715. static const char Binary2Base64[65] =
  716.             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  717. void PBase64::OutputBase64(const BYTE * data)
  718. {
  719.   char * out = encodedString.GetPointer((encodeLength&~255) + 256);
  720.   out[encodeLength++] = Binary2Base64[data[0] >> 2];
  721.   out[encodeLength++] = Binary2Base64[((data[0]&3)<<4) | (data[1]>>4)];
  722.   out[encodeLength++] = Binary2Base64[((data[1]&15)<<2) | (data[2]>>6)];
  723.   out[encodeLength++] = Binary2Base64[data[2]&0x3f];
  724.   if (++nextLine > 76) {
  725.     if (useCRLFs)
  726.       out[encodeLength++] = 'r';
  727.     out[encodeLength++] = 'n';
  728.     nextLine = 0;
  729.   }
  730. }
  731. void PBase64::ProcessEncoding(const void * dataPtr, PINDEX length)
  732. {
  733.   const BYTE * data = (const BYTE *)dataPtr;
  734.   while (saveCount < 3) {
  735.     saveTriple[saveCount++] = *data++;
  736.     if (--length <= 0)
  737.       return;
  738.   }
  739.   OutputBase64(saveTriple);
  740.   PINDEX i;
  741.   for (i = 0; i+2 < length; i += 3)
  742.     OutputBase64(data+i);
  743.   saveCount = length - i;
  744.   switch (saveCount) {
  745.     case 2 :
  746.       saveTriple[0] = data[i++];
  747.       saveTriple[1] = data[i];
  748.       break;
  749.     case 1 :
  750.       saveTriple[0] = data[i];
  751.   }
  752. }
  753. PString PBase64::GetEncodedString()
  754. {
  755.   PString retval = encodedString;
  756.   encodedString = "";
  757.   encodeLength = 0;
  758.   return retval;
  759. }
  760. PString PBase64::CompleteEncoding()
  761. {
  762.   char * out = encodedString.GetPointer(encodeLength + 5)+encodeLength;
  763.   switch (saveCount) {
  764.     case 1 :
  765.       *out++ = Binary2Base64[saveTriple[0] >> 2];
  766.       *out++ = Binary2Base64[(saveTriple[0]&3)<<4];
  767.       *out++ = '=';
  768.       *out   = '=';
  769.       break;
  770.     case 2 :
  771.       *out++ = Binary2Base64[saveTriple[0] >> 2];
  772.       *out++ = Binary2Base64[((saveTriple[0]&3)<<4) | (saveTriple[1]>>4)];
  773.       *out++ = Binary2Base64[((saveTriple[1]&15)<<2)];
  774.       *out   = '=';
  775.   }
  776.   return encodedString;
  777. }
  778. PString PBase64::Encode(const PString & str)
  779. {
  780.   return Encode((const char *)str);
  781. }
  782. PString PBase64::Encode(const char * cstr)
  783. {
  784.   return Encode((const BYTE *)cstr, strlen(cstr));
  785. }
  786. PString PBase64::Encode(const PBYTEArray & data)
  787. {
  788.   return Encode(data, data.GetSize());
  789. }
  790. PString PBase64::Encode(const void * data, PINDEX length)
  791. {
  792.   PBase64 encoder;
  793.   encoder.ProcessEncoding(data, length);
  794.   return encoder.CompleteEncoding();
  795. }
  796. void PBase64::StartDecoding()
  797. {
  798.   perfectDecode = TRUE;
  799.   quadPosition = 0;
  800.   decodedData.SetSize(0);
  801.   decodeSize = 0;
  802. }
  803. BOOL PBase64::ProcessDecoding(const PString & str)
  804. {
  805.   return ProcessDecoding((const char *)str);
  806. }
  807. BOOL PBase64::ProcessDecoding(const char * cstr)
  808. {
  809.   static const BYTE Base642Binary[256] = {
  810.     96, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 99, 99, 98, 99, 99,
  811.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  812.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
  813.     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 97, 99, 99,
  814.     99,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
  815.     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99,
  816.     99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  817.     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99,
  818.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  819.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  820.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  821.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  822.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  823.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  824.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
  825.     99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
  826.   };
  827.   for (;;) {
  828.     BYTE value = Base642Binary[(BYTE)*cstr++];
  829.     switch (value) {
  830.       case 96 : // end of string
  831.         return FALSE;
  832.       case 97 : // '=' sign
  833.         if (quadPosition == 3 || (quadPosition == 2 && *cstr == '=')) {
  834.           quadPosition = 0;  // Reset this to zero, as have a perfect decode
  835.           return TRUE; // Stop decoding now as must be at end of data
  836.         }
  837.         perfectDecode = FALSE;  // Ignore '=' sign but flag decode as suspect
  838.         break;
  839.       case 98 : // CRLFs
  840.         break;  // Ignore totally
  841.       case 99 :  // Illegal characters
  842.         perfectDecode = FALSE;  // Ignore rubbish but flag decode as suspect
  843.         break;
  844.       default : // legal value from 0 to 63
  845.         BYTE * out = decodedData.GetPointer(((decodeSize+1)&~255) + 256);
  846.         switch (quadPosition) {
  847.           case 0 :
  848.             out[decodeSize] = (BYTE)(value << 2);
  849.             break;
  850.           case 1 :
  851.             out[decodeSize++] |= (BYTE)(value >> 4);
  852.             out[decodeSize] = (BYTE)((value&15) << 4);
  853.             break;
  854.           case 2 :
  855.             out[decodeSize++] |= (BYTE)(value >> 2);
  856.             out[decodeSize] = (BYTE)((value&3) << 6);
  857.             break;
  858.           case 3 :
  859.             out[decodeSize++] |= (BYTE)value;
  860.             break;
  861.         }
  862.         quadPosition = (quadPosition+1)&3;
  863.     }
  864.   }
  865. }
  866. PBYTEArray PBase64::GetDecodedData()
  867. {
  868.   perfectDecode = quadPosition == 0;
  869.   decodedData.SetSize(decodeSize);
  870.   PBYTEArray retval = decodedData;
  871.   retval.MakeUnique();
  872.   decodedData.SetSize(0);
  873.   decodeSize = 0;
  874.   return retval;
  875. }
  876. BOOL PBase64::GetDecodedData(void * dataBlock, PINDEX length)
  877. {
  878.   perfectDecode = quadPosition == 0;
  879.   BOOL bigEnough = length >= decodeSize;
  880.   memcpy(dataBlock, decodedData, bigEnough ? decodeSize : length);
  881.   decodedData.SetSize(0);
  882.   decodeSize = 0;
  883.   return bigEnough;
  884. }
  885. PString PBase64::Decode(const PString & str)
  886. {
  887.   PBYTEArray data;
  888.   Decode(str, data);
  889.   return PString((const char *)(const BYTE *)data, data.GetSize());
  890. }
  891. BOOL PBase64::Decode(const PString & str, PBYTEArray & data)
  892. {
  893.   PBase64 decoder;
  894.   decoder.ProcessDecoding(str);
  895.   data = decoder.GetDecodedData();
  896.   return decoder.IsDecodeOK();
  897. }
  898. BOOL PBase64::Decode(const PString & str, void * dataBlock, PINDEX length)
  899. {
  900.   PBase64 decoder;
  901.   decoder.ProcessDecoding(str);
  902.   return decoder.GetDecodedData(dataBlock, length);
  903. }
  904. // End Of File ///////////////////////////////////////////////////////////////