Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / inetprot.cxx
blobde24e82e94fa806c070d7c701f7f15dd5439f580
1 /*
2 * inetprot.cxx
4 * Internet Protocol ancestor class.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
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/
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.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.55 2002/12/19 03:35:01 craigs
28 * Fixed problem with PInternetProtocol::Write returning wrong values
30 * Revision 1.54 2002/11/06 22:47:25 robertj
31 * Fixed header comment (copyright etc)
33 * Revision 1.53 2002/01/06 05:40:47 robertj
34 * Fixed wrong number of columns in base 64 encoder, thanks Lars Güsmar
36 * Revision 1.52 2001/10/03 00:25:25 robertj
37 * Split out function for adding a single line of MIME info, reduces
38 * duplicated code and is useful in some other areas such as HTTP/1.1
40 * Revision 1.51 2001/09/28 00:44:15 robertj
41 * Added SetInteger() function to set numeric MIME fields.
43 * Revision 1.50 2001/09/11 03:27:46 robertj
44 * Improved error processing on high level protocol failures, usually
45 * caused by unexpected shut down of a socket.
47 * Revision 1.49 2001/09/10 02:51:23 robertj
48 * Major change to fix problem with error codes being corrupted in a
49 * PChannel when have simultaneous reads and writes in threads.
51 * Revision 1.48 2000/11/27 00:58:06 robertj
52 * Fixed crash if used PBase64::ProcessEncoding() with zero length.
54 * Revision 1.47 2000/11/16 07:16:58 robertj
55 * Fixed maximum line length of base64 output to be 76 columns not 304.
57 * Revision 1.46 2000/11/14 08:28:44 robertj
58 * Fixed bug in base64 encoder, overwriting memory buffer.
60 * Revision 1.45 2000/05/05 10:08:29 robertj
61 * Fixed some GNU compiler warnings
63 * Revision 1.44 2000/05/02 08:29:07 craigs
64 * Removed "memory leaks" caused by brain-dead GNU linker
66 * Revision 1.43 1999/05/04 15:26:01 robertj
67 * Improved HTTP/1.1 compatibility (pass through user commands).
68 * Fixed problems with quicktime installer.
70 * Revision 1.42 1998/12/04 10:08:01 robertj
71 * Fixed bug in PMIMInfo read functions, should clear entries before loading.
73 * Revision 1.41 1998/11/30 04:52:02 robertj
74 * New directory structure
76 * Revision 1.40 1998/11/03 01:03:09 robertj
77 * Fixed problem with multiline response that is non-numeric.
79 * Revision 1.39 1998/10/16 02:05:55 robertj
80 * Tried to make ReadLine more forgiving of CR CR LF combination.
82 * Revision 1.38 1998/09/23 06:22:20 robertj
83 * Added open source copyright license.
85 * Revision 1.37 1998/07/24 06:55:00 robertj
86 * Improved robustness of base64 decoding.
88 * Revision 1.36 1998/02/03 06:20:25 robertj
89 * Fixed bug in Accept() function passing on to IP Accept().
91 * Revision 1.35 1998/01/26 02:49:20 robertj
92 * GNU support.
94 * Revision 1.34 1998/01/26 00:46:48 robertj
95 * Fixed Connect functions on PInternetProtocol so propagates read timeout variable so can adjust the connect timeout..
97 * Revision 1.33 1997/11/06 10:26:48 robertj
98 * Fixed bug in debug dump of MIME dictionary, did not have linefeeds between entries.
100 * Revision 1.32 1997/06/09 04:30:03 robertj
101 * Fixed multiple MIME field bug.
103 * Revision 1.31 1997/06/06 08:53:51 robertj
104 * Fixed bug with multiple cookies (MIME fields) are sent to IE.
106 * Revision 1.30 1997/03/28 13:04:37 robertj
107 * Fixed bug for multiple fields in MIME headers, especially cookies.
109 * Revision 1.29 1997/03/18 21:26:46 robertj
110 * Fixed stream write of MIME putting double CR's in text files..
112 * Revision 1.28 1997/02/05 11:53:13 robertj
113 * Changed construction of MIME dictionary to be delayed untill it is used.
115 * Revision 1.27 1996/12/05 11:41:12 craigs
116 * Fix problem with STAT command response containing lines not starting
117 * with response number
119 * Revision 1.26 1996/10/08 13:07:39 robertj
120 * Changed default for assert to be ignore, not abort.
122 * Revision 1.25 1996/09/16 12:57:07 robertj
123 * Fixed missing propagationof errors on open of subchannel.
125 * Revision 1.24 1996/09/14 13:09:36 robertj
126 * Major upgrade:
127 * rearranged sockets to help support IPX.
128 * added indirect channel class and moved all protocols to descend from it,
129 * separating the protocol from the low level byte transport.
131 * Revision 1.23 1996/08/25 09:35:47 robertj
132 * Added bug in appsock that last response is set on an I/O error.
134 * Revision 1.22 1996/07/15 10:33:14 robertj
135 * Changed memory block base64 conversion functions to be void *.
137 * Revision 1.21 1996/06/03 11:58:43 robertj
138 * Fixed bug in reading successive UnRead() calls getting save in wrong order.
140 * Revision 1.20 1996/05/26 03:46:22 robertj
141 * Compatibility to GNU 2.7.x
143 * Revision 1.19 1996/05/15 10:15:15 robertj
144 * Added access function to set intercharacter line read timeout.
146 * Revision 1.18 1996/05/09 12:14:04 robertj
147 * Rewrote the "unread" buffer usage and then used it to improve ReadLine() performance.
149 * Revision 1.17 1996/03/31 08:57:34 robertj
150 * Changed MIME type for no extension from binary to text.
151 * Added flush of data before sending a command.
152 * Added version of WriteCommand() and ExecteCommand() without argument string.
154 * Revision 1.15 1996/03/18 13:33:13 robertj
155 * Fixed incompatibilities to GNU compiler where PINDEX != int.
157 * Revision 1.14 1996/03/16 04:53:07 robertj
158 * Changed all the get host name and get host address functions to be more consistent.
159 * Added ParseReponse() for splitting reponse line into code and info.
160 * Changed lastResponseCode to an integer.
161 * Fixed bug in MIME write function, should be const.
162 * Added PString parameter version of UnRead().
164 * Revision 1.13 1996/03/04 12:20:41 robertj
165 * Split file into mailsock.cxx
167 * Revision 1.12 1996/02/25 11:16:07 robertj
168 * Fixed bug in ReadResponse() for multi-line responses under FTP..
170 * Revision 1.11 1996/02/25 03:05:12 robertj
171 * Added decoding of Base64 to a block of memory instead of PBYTEArray.
173 * Revision 1.10 1996/02/19 13:31:26 robertj
174 * Changed stuff to use new & operator..
176 * Revision 1.9 1996/02/15 14:42:41 robertj
177 * Fixed warning for long to int conversion.
179 * Revision 1.8 1996/02/13 12:57:49 robertj
180 * Added access to the last response in an application socket.
182 * Revision 1.7 1996/02/03 11:33:17 robertj
183 * Changed RadCmd() so can distinguish between I/O error and unknown command.
185 * Revision 1.6 1996/01/28 14:11:11 robertj
186 * Fixed bug in MIME content types for non caseless strings.
187 * Added default value in string for service name.
189 * Revision 1.5 1996/01/28 02:48:27 robertj
190 * Removal of MemoryPointer classes as usage didn't work for GNU.
192 * Revision 1.4 1996/01/26 02:24:29 robertj
193 * Further implemetation.
195 * Revision 1.3 1996/01/23 13:18:43 robertj
196 * Major rewrite for HTTP support.
198 * Revision 1.2 1995/11/09 12:19:29 robertj
199 * Fixed missing state assertion in state machine.
201 * Revision 1.1 1995/06/17 00:50:37 robertj
202 * Initial revision
206 #ifdef __GNUC__
207 #pragma implementation "inetprot.h"
208 #pragma implementation "mime.h"
209 #endif
211 #include <ptlib.h>
212 #include <ptlib/sockets.h>
213 #include <ptclib/inetprot.h>
214 #include <ptclib/mime.h>
217 static const char * CRLF = "\r\n";
220 //////////////////////////////////////////////////////////////////////////////
221 // PInternetProtocol
223 PInternetProtocol::PInternetProtocol(const char * svcName,
224 PINDEX cmdCount,
225 char const * const * cmdNames)
226 : defaultServiceName(svcName),
227 commandNames(cmdCount, cmdNames, TRUE),
228 readLineTimeout(0, 10) // 10 seconds
230 SetReadTimeout(PTimeInterval(0, 0, 10)); // 10 minutes
231 stuffingState = DontStuff;
232 newLineToCRLF = TRUE;
233 unReadCount = 0;
237 void PInternetProtocol::SetReadLineTimeout(const PTimeInterval & t)
239 readLineTimeout = t;
243 BOOL PInternetProtocol::Read(void * buf, PINDEX len)
245 lastReadCount = PMIN(unReadCount, len);
246 const char * unReadPtr = ((const char *)unReadBuffer)+unReadCount;
247 char * bufptr = (char *)buf;
248 while (unReadCount > 0 && len > 0) {
249 *bufptr++ = *--unReadPtr;
250 unReadCount--;
251 len--;
254 if (unReadCount == 0)
255 unReadBuffer.SetSize(0);
257 if (len > 0) {
258 PINDEX saveCount = lastReadCount;
259 PIndirectChannel::Read(bufptr, len);
260 lastReadCount += saveCount;
263 return lastReadCount > 0;
267 BOOL PInternetProtocol::Write(const void * buf, PINDEX len)
269 if (len == 0 || stuffingState == DontStuff)
270 return PIndirectChannel::Write(buf, len);
272 PINDEX totalWritten = 0;
273 const char * base = (const char *)buf;
274 const char * current = base;
275 while (len-- > 0) {
276 switch (stuffingState) {
277 case StuffIdle :
278 switch (*current) {
279 case '\r' :
280 stuffingState = StuffCR;
281 break;
283 case '\n' :
284 if (newLineToCRLF) {
285 if (current > base) {
286 if (!PIndirectChannel::Write(base, current - base))
287 return FALSE;
288 totalWritten += lastWriteCount;
290 if (!PIndirectChannel::Write("\r", 1))
291 return FALSE;
292 totalWritten += lastWriteCount;
293 base = current;
296 break;
298 case StuffCR :
299 stuffingState = *current != '\n' ? StuffIdle : StuffCRLF;
300 break;
302 case StuffCRLF :
303 if (*current == '.') {
304 if (current > base) {
305 if (!PIndirectChannel::Write(base, current - base))
306 return FALSE;
307 totalWritten += lastWriteCount;
309 if (!PIndirectChannel::Write(".", 1))
310 return FALSE;
311 totalWritten += lastWriteCount;
312 base = current;
314 // Then do default state
316 default :
317 stuffingState = StuffIdle;
318 break;
320 current++;
323 if (current > base) {
324 if (!PIndirectChannel::Write(base, current - base))
325 return FALSE;
326 totalWritten += lastWriteCount;
329 lastWriteCount = totalWritten;
330 return lastWriteCount > 0;
334 BOOL PInternetProtocol::AttachSocket(PIPSocket * socket)
336 if (socket->IsOpen()) {
337 if (Open(socket))
338 return TRUE;
339 Close();
340 SetErrorValues(Miscellaneous, 0x41000000);
342 else {
343 SetErrorValues(socket->GetErrorCode(), socket->GetErrorNumber());
344 delete socket;
347 return FALSE;
351 BOOL PInternetProtocol::Connect(const PString & address, WORD port)
353 if (port == 0)
354 return Connect(address, defaultServiceName);
356 if (readTimeout == PMaxTimeInterval)
357 return AttachSocket(new PTCPSocket(address, port));
359 PTCPSocket * s = new PTCPSocket(port);
360 s->SetReadTimeout(readTimeout);
361 s->Connect(address);
362 return AttachSocket(s);
366 BOOL PInternetProtocol::Connect(const PString & address, const PString & service)
368 if (readTimeout == PMaxTimeInterval)
369 return AttachSocket(new PTCPSocket(address, service));
371 PTCPSocket * s = new PTCPSocket;
372 s->SetReadTimeout(readTimeout);
373 s->SetPort(service);
374 s->Connect(address);
375 return AttachSocket(s);
379 BOOL PInternetProtocol::Accept(PSocket & listener)
381 if (readTimeout == PMaxTimeInterval)
382 return AttachSocket(new PTCPSocket(listener));
384 PTCPSocket * s = new PTCPSocket;
385 s->SetReadTimeout(readTimeout);
386 s->Accept(listener);
387 return AttachSocket(s);
391 const PString & PInternetProtocol::GetDefaultService() const
393 return defaultServiceName;
397 PIPSocket * PInternetProtocol::GetSocket() const
399 PChannel * channel = GetBaseReadChannel();
400 if (channel != NULL && channel->IsDescendant(PIPSocket::Class()))
401 return (PIPSocket *)channel;
402 return NULL;
406 BOOL PInternetProtocol::WriteLine(const PString & line)
408 if (line.FindOneOf(CRLF) == P_MAX_INDEX)
409 return WriteString(line + CRLF);
411 PStringArray lines = line.Lines();
412 for (PINDEX i = 0; i < lines.GetSize(); i++)
413 if (!WriteString(lines[i] + CRLF))
414 return FALSE;
416 return TRUE;
420 BOOL PInternetProtocol::ReadLine(PString & str, BOOL allowContinuation)
422 str = PString();
424 PCharArray line(100);
425 PINDEX count = 0;
426 BOOL gotEndOfLine = FALSE;
428 int c = ReadChar();
429 if (c < 0)
430 return FALSE;
432 PTimeInterval oldTimeout = GetReadTimeout();
433 SetReadTimeout(readLineTimeout);
435 while (c >= 0 && !gotEndOfLine) {
436 if (unReadCount == 0) {
437 char readAhead[1000];
438 SetReadTimeout(0);
439 if (PIndirectChannel::Read(readAhead, sizeof(readAhead)))
440 UnRead(readAhead, GetLastReadCount());
441 SetReadTimeout(readLineTimeout);
443 switch (c) {
444 case '\b' :
445 case '\177' :
446 if (count > 0)
447 count--;
448 c = ReadChar();
449 break;
451 case '\r' :
452 c = ReadChar();
453 switch (c) {
454 case -1 :
455 case '\n' :
456 break;
458 case '\r' :
459 c = ReadChar();
460 if (c == '\n')
461 break;
462 UnRead(c);
463 c = '\r';
464 // Then do default case
466 default :
467 UnRead(c);
469 // Then do line feed case
471 case '\n' :
472 if (count == 0 || !allowContinuation || (c = ReadChar()) < 0)
473 gotEndOfLine = TRUE;
474 else if (c != ' ' && c != '\t') {
475 UnRead(c);
476 gotEndOfLine = TRUE;
478 break;
480 default :
481 if (count >= line.GetSize())
482 line.SetSize(count + 100);
483 line[count++] = (char)c;
484 c = ReadChar();
488 SetReadTimeout(oldTimeout);
490 if (count > 0)
491 str = PString(line, count);
492 return gotEndOfLine;
496 void PInternetProtocol::UnRead(int ch)
498 unReadBuffer.SetSize((unReadCount+256)&~255);
499 unReadBuffer[unReadCount++] = (char)ch;
503 void PInternetProtocol::UnRead(const PString & str)
505 UnRead((const char *)str, str.GetLength());
509 void PInternetProtocol::UnRead(const void * buffer, PINDEX len)
511 char * unreadptr =
512 unReadBuffer.GetPointer((unReadCount+len+255)&~255)+unReadCount;
513 const char * bufptr = ((const char *)buffer)+len;
514 unReadCount += len;
515 while (len-- > 0)
516 *unreadptr++ = *--bufptr;
520 BOOL PInternetProtocol::WriteCommand(PINDEX cmdNumber)
522 if (cmdNumber >= commandNames.GetSize())
523 return FALSE;
524 return WriteLine(commandNames[cmdNumber]);
528 BOOL PInternetProtocol::WriteCommand(PINDEX cmdNumber, const PString & param)
530 if (cmdNumber >= commandNames.GetSize())
531 return FALSE;
532 if (param.IsEmpty())
533 return WriteLine(commandNames[cmdNumber]);
534 else
535 return WriteLine(commandNames[cmdNumber] & param);
539 BOOL PInternetProtocol::ReadCommand(PINDEX & num, PString & args)
541 do {
542 if (!ReadLine(args))
543 return FALSE;
544 } while (args.IsEmpty());
546 PINDEX endCommand = args.Find(' ');
547 if (endCommand == P_MAX_INDEX)
548 endCommand = args.GetLength();
549 PCaselessString cmd = args.Left(endCommand);
551 num = commandNames.GetValuesIndex(cmd);
552 if (num != P_MAX_INDEX)
553 args = args.Mid(endCommand+1);
555 return TRUE;
559 BOOL PInternetProtocol::WriteResponse(unsigned code, const PString & info)
561 return WriteResponse(psprintf("%03u", code), info);
565 BOOL PInternetProtocol::WriteResponse(const PString & code,
566 const PString & info)
568 if (info.FindOneOf(CRLF) == P_MAX_INDEX)
569 return WriteString(code & info + CRLF);
571 PStringArray lines = info.Lines();
572 PINDEX i;
573 for (i = 0; i < lines.GetSize()-1; i++)
574 if (!WriteString(code + '-' + lines[i] + CRLF))
575 return FALSE;
577 return WriteString(code & lines[i] + CRLF);
581 BOOL PInternetProtocol::ReadResponse()
583 PString line;
584 if (!ReadLine(line)) {
585 lastResponseCode = -1;
586 if (GetErrorCode(LastReadError) != NoError)
587 lastResponseInfo = GetErrorText(LastReadError);
588 else {
589 lastResponseInfo = "Remote shutdown";
590 SetErrorValues(ProtocolFailure, 0, LastReadError);
592 return FALSE;
595 PINDEX continuePos = ParseResponse(line);
596 if (continuePos == 0)
597 return TRUE;
599 PString prefix = line.Left(continuePos);
600 char continueChar = line[continuePos];
601 while (line[continuePos] == continueChar ||
602 (!isdigit(line[0]) && strncmp(line, prefix, continuePos) != 0)) {
603 lastResponseInfo += '\n';
604 if (!ReadLine(line)) {
605 if (GetErrorCode(LastReadError) != NoError)
606 lastResponseInfo += GetErrorText(LastReadError);
607 else
608 SetErrorValues(ProtocolFailure, 0, LastReadError);
609 return FALSE;
611 if (line.Left(continuePos) == prefix)
612 lastResponseInfo += line.Mid(continuePos+1);
613 else
614 lastResponseInfo += line;
617 return TRUE;
621 BOOL PInternetProtocol::ReadResponse(int & code, PString & info)
623 BOOL retval = ReadResponse();
625 code = lastResponseCode;
626 info = lastResponseInfo;
628 return retval;
632 PINDEX PInternetProtocol::ParseResponse(const PString & line)
634 PINDEX endCode = line.FindOneOf(" -");
635 if (endCode == P_MAX_INDEX) {
636 lastResponseCode = -1;
637 lastResponseInfo = line;
638 return 0;
641 lastResponseCode = line.Left(endCode).AsInteger();
642 lastResponseInfo = line.Mid(endCode+1);
643 return line[endCode] != ' ' ? endCode : 0;
647 int PInternetProtocol::ExecuteCommand(PINDEX cmd)
649 return ExecuteCommand(cmd, PString());
653 int PInternetProtocol::ExecuteCommand(PINDEX cmd,
654 const PString & param)
656 PTimeInterval oldTimeout = GetReadTimeout();
657 SetReadTimeout(0);
658 while (ReadChar() >= 0)
660 SetReadTimeout(oldTimeout);
661 return WriteCommand(cmd, param) && ReadResponse() ? lastResponseCode : -1;
665 int PInternetProtocol::GetLastResponseCode() const
667 return lastResponseCode;
671 PString PInternetProtocol::GetLastResponseInfo() const
673 return lastResponseInfo;
677 //////////////////////////////////////////////////////////////////////////////
678 // PMIMEInfo
680 PMIMEInfo::PMIMEInfo(istream & strm)
682 ReadFrom(strm);
686 PMIMEInfo::PMIMEInfo(PInternetProtocol & socket)
688 Read(socket);
692 void PMIMEInfo::PrintOn(ostream &strm) const
694 BOOL output_cr = strm.fill() == '\r';
695 strm.fill(' ');
696 for (PINDEX i = 0; i < GetSize(); i++) {
697 PString name = GetKeyAt(i) + ": ";
698 PString value = GetDataAt(i);
699 if (value.FindOneOf("\r\n") != P_MAX_INDEX) {
700 PStringArray vals = value.Lines();
701 for (PINDEX j = 0; j < vals.GetSize(); j++) {
702 strm << name << vals[j];
703 if (output_cr)
704 strm << '\r';
705 strm << '\n';
708 else {
709 strm << name << value;
710 if (output_cr)
711 strm << '\r';
712 strm << '\n';
715 if (output_cr)
716 strm << '\r';
717 strm << endl;
721 void PMIMEInfo::ReadFrom(istream &strm)
723 RemoveAll();
725 PString line;
726 while (strm.good()) {
727 strm >> line;
728 if (line.IsEmpty())
729 break;
730 AddMIME(line);
735 BOOL PMIMEInfo::Read(PInternetProtocol & socket)
737 RemoveAll();
739 PString line;
740 while (socket.ReadLine(line, TRUE)) {
741 if (line.IsEmpty())
742 return TRUE;
743 AddMIME(line);
746 return FALSE;
750 BOOL PMIMEInfo::AddMIME(const PString & line)
752 PINDEX colonPos = line.Find(':');
753 if (colonPos == P_MAX_INDEX)
754 return FALSE;
756 PCaselessString fieldName = line.Left(colonPos).Trim();
757 PString fieldValue = line(colonPos+1, P_MAX_INDEX).Trim();
759 if (Contains(fieldName))
760 fieldValue = (*this)[fieldName] + '\n' + fieldValue;
762 SetAt(fieldName, fieldValue);
764 return TRUE;
768 BOOL PMIMEInfo::Write(PInternetProtocol & socket) const
770 for (PINDEX i = 0; i < GetSize(); i++) {
771 PString name = GetKeyAt(i) + ": ";
772 PString value = GetDataAt(i);
773 if (value.FindOneOf("\r\n") != P_MAX_INDEX) {
774 PStringArray vals = value.Lines();
775 for (PINDEX j = 0; j < vals.GetSize(); j++) {
776 if (!socket.WriteLine(name + vals[j]))
777 return FALSE;
780 else {
781 if (!socket.WriteLine(name + value))
782 return FALSE;
786 return socket.WriteString(CRLF);
790 PString PMIMEInfo::GetString(const PString & key, const PString & dflt) const
792 if (GetAt(PCaselessString(key)) == NULL)
793 return dflt;
794 return operator[](key);
798 long PMIMEInfo::GetInteger(const PString & key, long dflt) const
800 if (GetAt(PCaselessString(key)) == NULL)
801 return dflt;
802 return operator[](key).AsInteger();
806 void PMIMEInfo::SetInteger(const PCaselessString & key, long value)
808 SetAt(key, PString(PString::Unsigned, value));
812 static const PStringToString::Initialiser DefaultContentTypes[] = {
813 { ".txt", "text/plain" },
814 { ".text", "text/plain" },
815 { ".html", "text/html" },
816 { ".htm", "text/html" },
817 { ".aif", "audio/aiff" },
818 { ".aiff", "audio/aiff" },
819 { ".au", "audio/basic" },
820 { ".snd", "audio/basic" },
821 { ".wav", "audio/wav" },
822 { ".gif", "image/gif" },
823 { ".xbm", "image/x-bitmap" },
824 { ".tif", "image/tiff" },
825 { ".tiff", "image/tiff" },
826 { ".jpg", "image/jpeg" },
827 { ".jpe", "image/jpeg" },
828 { ".jpeg", "image/jpeg" },
829 { ".avi", "video/avi" },
830 { ".mpg", "video/mpeg" },
831 { ".mpeg", "video/mpeg" },
832 { ".qt", "video/quicktime" },
833 { ".mov", "video/quicktime" }
836 PStringToString & PMIMEInfo::GetContentTypes()
838 static PStringToString contentTypes(PARRAYSIZE(DefaultContentTypes),
839 DefaultContentTypes,
840 TRUE);
841 return contentTypes;
845 void PMIMEInfo::SetAssociation(const PStringToString & allTypes, BOOL merge)
847 PStringToString & types = GetContentTypes();
848 if (!merge)
849 types.RemoveAll();
850 for (PINDEX i = 0; i < allTypes.GetSize(); i++)
851 types.SetAt(allTypes.GetKeyAt(i), allTypes.GetDataAt(i));
855 PString PMIMEInfo::GetContentType(const PString & fType)
857 if (fType.IsEmpty())
858 return "text/plain";
860 PStringToString & types = GetContentTypes();
861 if (types.Contains(fType))
862 return types[fType];
864 return "application/octet-stream";
868 ///////////////////////////////////////////////////////////////////////////////
869 // PBase64
871 PBase64::PBase64()
873 StartEncoding();
874 StartDecoding();
878 void PBase64::StartEncoding(BOOL useCRLF)
880 encodedString = "";
881 encodeLength = nextLine = saveCount = 0;
882 useCRLFs = useCRLF;
886 void PBase64::ProcessEncoding(const PString & str)
888 ProcessEncoding((const char *)str);
892 void PBase64::ProcessEncoding(const char * cstr)
894 ProcessEncoding((const BYTE *)cstr, strlen(cstr));
898 void PBase64::ProcessEncoding(const PBYTEArray & data)
900 ProcessEncoding(data, data.GetSize());
904 static const char Binary2Base64[65] =
905 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
907 void PBase64::OutputBase64(const BYTE * data)
909 char * out = encodedString.GetPointer(((encodeLength+7)&~255) + 256);
911 out[encodeLength++] = Binary2Base64[data[0] >> 2];
912 out[encodeLength++] = Binary2Base64[((data[0]&3)<<4) | (data[1]>>4)];
913 out[encodeLength++] = Binary2Base64[((data[1]&15)<<2) | (data[2]>>6)];
914 out[encodeLength++] = Binary2Base64[data[2]&0x3f];
916 if (++nextLine > 18) { // 76 columns
917 if (useCRLFs)
918 out[encodeLength++] = '\r';
919 out[encodeLength++] = '\n';
920 nextLine = 0;
925 void PBase64::ProcessEncoding(const void * dataPtr, PINDEX length)
927 if (length == 0)
928 return;
930 const BYTE * data = (const BYTE *)dataPtr;
931 while (saveCount < 3) {
932 saveTriple[saveCount++] = *data++;
933 if (--length == 0)
934 return;
937 OutputBase64(saveTriple);
939 PINDEX i;
940 for (i = 0; i+2 < length; i += 3)
941 OutputBase64(data+i);
943 saveCount = length - i;
944 switch (saveCount) {
945 case 2 :
946 saveTriple[0] = data[i++];
947 saveTriple[1] = data[i];
948 break;
949 case 1 :
950 saveTriple[0] = data[i];
955 PString PBase64::GetEncodedString()
957 PString retval = encodedString;
958 encodedString = "";
959 encodeLength = 0;
960 return retval;
964 PString PBase64::CompleteEncoding()
966 char * out = encodedString.GetPointer(encodeLength + 5)+encodeLength;
968 switch (saveCount) {
969 case 1 :
970 *out++ = Binary2Base64[saveTriple[0] >> 2];
971 *out++ = Binary2Base64[(saveTriple[0]&3)<<4];
972 *out++ = '=';
973 *out = '=';
974 break;
976 case 2 :
977 *out++ = Binary2Base64[saveTriple[0] >> 2];
978 *out++ = Binary2Base64[((saveTriple[0]&3)<<4) | (saveTriple[1]>>4)];
979 *out++ = Binary2Base64[((saveTriple[1]&15)<<2)];
980 *out = '=';
983 return encodedString;
987 PString PBase64::Encode(const PString & str)
989 return Encode((const char *)str);
993 PString PBase64::Encode(const char * cstr)
995 return Encode((const BYTE *)cstr, strlen(cstr));
999 PString PBase64::Encode(const PBYTEArray & data)
1001 return Encode(data, data.GetSize());
1005 PString PBase64::Encode(const void * data, PINDEX length)
1007 PBase64 encoder;
1008 encoder.ProcessEncoding(data, length);
1009 return encoder.CompleteEncoding();
1013 void PBase64::StartDecoding()
1015 perfectDecode = TRUE;
1016 quadPosition = 0;
1017 decodedData.SetSize(0);
1018 decodeSize = 0;
1022 BOOL PBase64::ProcessDecoding(const PString & str)
1024 return ProcessDecoding((const char *)str);
1028 BOOL PBase64::ProcessDecoding(const char * cstr)
1030 static const BYTE Base642Binary[256] = {
1031 96, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 99, 99, 98, 99, 99,
1032 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1033 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
1034 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 97, 99, 99,
1035 99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
1036 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99,
1037 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1038 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99,
1039 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1040 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1041 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1042 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1043 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1044 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1045 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
1046 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
1049 for (;;) {
1050 BYTE value = Base642Binary[(BYTE)*cstr++];
1051 switch (value) {
1052 case 96 : // end of string
1053 return FALSE;
1055 case 97 : // '=' sign
1056 if (quadPosition == 3 || (quadPosition == 2 && *cstr == '=')) {
1057 quadPosition = 0; // Reset this to zero, as have a perfect decode
1058 return TRUE; // Stop decoding now as must be at end of data
1060 perfectDecode = FALSE; // Ignore '=' sign but flag decode as suspect
1061 break;
1063 case 98 : // CRLFs
1064 break; // Ignore totally
1066 case 99 : // Illegal characters
1067 perfectDecode = FALSE; // Ignore rubbish but flag decode as suspect
1068 break;
1070 default : // legal value from 0 to 63
1071 BYTE * out = decodedData.GetPointer(((decodeSize+1)&~255) + 256);
1072 switch (quadPosition) {
1073 case 0 :
1074 out[decodeSize] = (BYTE)(value << 2);
1075 break;
1076 case 1 :
1077 out[decodeSize++] |= (BYTE)(value >> 4);
1078 out[decodeSize] = (BYTE)((value&15) << 4);
1079 break;
1080 case 2 :
1081 out[decodeSize++] |= (BYTE)(value >> 2);
1082 out[decodeSize] = (BYTE)((value&3) << 6);
1083 break;
1084 case 3 :
1085 out[decodeSize++] |= (BYTE)value;
1086 break;
1088 quadPosition = (quadPosition+1)&3;
1094 PBYTEArray PBase64::GetDecodedData()
1096 perfectDecode = quadPosition == 0;
1097 decodedData.SetSize(decodeSize);
1098 PBYTEArray retval = decodedData;
1099 retval.MakeUnique();
1100 decodedData.SetSize(0);
1101 decodeSize = 0;
1102 return retval;
1106 BOOL PBase64::GetDecodedData(void * dataBlock, PINDEX length)
1108 perfectDecode = quadPosition == 0;
1109 BOOL bigEnough = length >= decodeSize;
1110 memcpy(dataBlock, decodedData, bigEnough ? decodeSize : length);
1111 decodedData.SetSize(0);
1112 decodeSize = 0;
1113 return bigEnough;
1117 PString PBase64::Decode(const PString & str)
1119 PBYTEArray data;
1120 Decode(str, data);
1121 return PString((const char *)(const BYTE *)data, data.GetSize());
1125 BOOL PBase64::Decode(const PString & str, PBYTEArray & data)
1127 PBase64 decoder;
1128 decoder.ProcessDecoding(str);
1129 data = decoder.GetDecodedData();
1130 return decoder.IsDecodeOK();
1134 BOOL PBase64::Decode(const PString & str, void * dataBlock, PINDEX length)
1136 PBase64 decoder;
1137 decoder.ProcessDecoding(str);
1138 return decoder.GetDecodedData(dataBlock, length);
1142 // End Of File ///////////////////////////////////////////////////////////////