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
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
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
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
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
207 #pragma implementation "inetprot.h"
208 #pragma implementation "mime.h"
212 #include <ptlib/sockets.h>
213 #include <ptclib/inetprot.h>
214 #include <ptclib/mime.h>
217 static const char * CRLF
= "\r\n";
220 //////////////////////////////////////////////////////////////////////////////
223 PInternetProtocol::PInternetProtocol(const char * svcName
,
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
;
237 void PInternetProtocol::SetReadLineTimeout(const PTimeInterval
& 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
;
254 if (unReadCount
== 0)
255 unReadBuffer
.SetSize(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
;
276 switch (stuffingState
) {
280 stuffingState
= StuffCR
;
285 if (current
> base
) {
286 if (!PIndirectChannel::Write(base
, current
- base
))
288 totalWritten
+= lastWriteCount
;
290 if (!PIndirectChannel::Write("\r", 1))
292 totalWritten
+= lastWriteCount
;
299 stuffingState
= *current
!= '\n' ? StuffIdle
: StuffCRLF
;
303 if (*current
== '.') {
304 if (current
> base
) {
305 if (!PIndirectChannel::Write(base
, current
- base
))
307 totalWritten
+= lastWriteCount
;
309 if (!PIndirectChannel::Write(".", 1))
311 totalWritten
+= lastWriteCount
;
314 // Then do default state
317 stuffingState
= StuffIdle
;
323 if (current
> base
) {
324 if (!PIndirectChannel::Write(base
, current
- base
))
326 totalWritten
+= lastWriteCount
;
329 lastWriteCount
= totalWritten
;
330 return lastWriteCount
> 0;
334 BOOL
PInternetProtocol::AttachSocket(PIPSocket
* socket
)
336 if (socket
->IsOpen()) {
340 SetErrorValues(Miscellaneous
, 0x41000000);
343 SetErrorValues(socket
->GetErrorCode(), socket
->GetErrorNumber());
351 BOOL
PInternetProtocol::Connect(const PString
& address
, WORD port
)
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
);
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
);
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
);
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
;
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
))
420 BOOL
PInternetProtocol::ReadLine(PString
& str
, BOOL allowContinuation
)
424 PCharArray
line(100);
426 BOOL gotEndOfLine
= FALSE
;
432 PTimeInterval oldTimeout
= GetReadTimeout();
433 SetReadTimeout(readLineTimeout
);
435 while (c
>= 0 && !gotEndOfLine
) {
436 if (unReadCount
== 0) {
437 char readAhead
[1000];
439 if (PIndirectChannel::Read(readAhead
, sizeof(readAhead
)))
440 UnRead(readAhead
, GetLastReadCount());
441 SetReadTimeout(readLineTimeout
);
464 // Then do default case
469 // Then do line feed case
472 if (count
== 0 || !allowContinuation
|| (c
= ReadChar()) < 0)
474 else if (c
!= ' ' && c
!= '\t') {
481 if (count
>= line
.GetSize())
482 line
.SetSize(count
+ 100);
483 line
[count
++] = (char)c
;
488 SetReadTimeout(oldTimeout
);
491 str
= PString(line
, count
);
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
)
512 unReadBuffer
.GetPointer((unReadCount
+len
+255)&~255)+unReadCount
;
513 const char * bufptr
= ((const char *)buffer
)+len
;
516 *unreadptr
++ = *--bufptr
;
520 BOOL
PInternetProtocol::WriteCommand(PINDEX cmdNumber
)
522 if (cmdNumber
>= commandNames
.GetSize())
524 return WriteLine(commandNames
[cmdNumber
]);
528 BOOL
PInternetProtocol::WriteCommand(PINDEX cmdNumber
, const PString
& param
)
530 if (cmdNumber
>= commandNames
.GetSize())
533 return WriteLine(commandNames
[cmdNumber
]);
535 return WriteLine(commandNames
[cmdNumber
] & param
);
539 BOOL
PInternetProtocol::ReadCommand(PINDEX
& num
, PString
& args
)
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);
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();
573 for (i
= 0; i
< lines
.GetSize()-1; i
++)
574 if (!WriteString(code
+ '-' + lines
[i
] + CRLF
))
577 return WriteString(code
& lines
[i
] + CRLF
);
581 BOOL
PInternetProtocol::ReadResponse()
584 if (!ReadLine(line
)) {
585 lastResponseCode
= -1;
586 if (GetErrorCode(LastReadError
) != NoError
)
587 lastResponseInfo
= GetErrorText(LastReadError
);
589 lastResponseInfo
= "Remote shutdown";
590 SetErrorValues(ProtocolFailure
, 0, LastReadError
);
595 PINDEX continuePos
= ParseResponse(line
);
596 if (continuePos
== 0)
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
);
608 SetErrorValues(ProtocolFailure
, 0, LastReadError
);
611 if (line
.Left(continuePos
) == prefix
)
612 lastResponseInfo
+= line
.Mid(continuePos
+1);
614 lastResponseInfo
+= line
;
621 BOOL
PInternetProtocol::ReadResponse(int & code
, PString
& info
)
623 BOOL retval
= ReadResponse();
625 code
= lastResponseCode
;
626 info
= lastResponseInfo
;
632 PINDEX
PInternetProtocol::ParseResponse(const PString
& line
)
634 PINDEX endCode
= line
.FindOneOf(" -");
635 if (endCode
== P_MAX_INDEX
) {
636 lastResponseCode
= -1;
637 lastResponseInfo
= line
;
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();
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 //////////////////////////////////////////////////////////////////////////////
680 PMIMEInfo::PMIMEInfo(istream
& strm
)
686 PMIMEInfo::PMIMEInfo(PInternetProtocol
& socket
)
692 void PMIMEInfo::PrintOn(ostream
&strm
) const
694 BOOL output_cr
= strm
.fill() == '\r';
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
];
709 strm
<< name
<< value
;
721 void PMIMEInfo::ReadFrom(istream
&strm
)
726 while (strm
.good()) {
735 BOOL
PMIMEInfo::Read(PInternetProtocol
& socket
)
740 while (socket
.ReadLine(line
, TRUE
)) {
750 BOOL
PMIMEInfo::AddMIME(const PString
& line
)
752 PINDEX colonPos
= line
.Find(':');
753 if (colonPos
== P_MAX_INDEX
)
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
);
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
]))
781 if (!socket
.WriteLine(name
+ value
))
786 return socket
.WriteString(CRLF
);
790 PString
PMIMEInfo::GetString(const PString
& key
, const PString
& dflt
) const
792 if (GetAt(PCaselessString(key
)) == NULL
)
794 return operator[](key
);
798 long PMIMEInfo::GetInteger(const PString
& key
, long dflt
) const
800 if (GetAt(PCaselessString(key
)) == NULL
)
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
),
845 void PMIMEInfo::SetAssociation(const PStringToString
& allTypes
, BOOL merge
)
847 PStringToString
& types
= GetContentTypes();
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
)
860 PStringToString
& types
= GetContentTypes();
861 if (types
.Contains(fType
))
864 return "application/octet-stream";
868 ///////////////////////////////////////////////////////////////////////////////
878 void PBase64::StartEncoding(BOOL useCRLF
)
881 encodeLength
= nextLine
= saveCount
= 0;
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
918 out
[encodeLength
++] = '\r';
919 out
[encodeLength
++] = '\n';
925 void PBase64::ProcessEncoding(const void * dataPtr
, PINDEX length
)
930 const BYTE
* data
= (const BYTE
*)dataPtr
;
931 while (saveCount
< 3) {
932 saveTriple
[saveCount
++] = *data
++;
937 OutputBase64(saveTriple
);
940 for (i
= 0; i
+2 < length
; i
+= 3)
941 OutputBase64(data
+i
);
943 saveCount
= length
- i
;
946 saveTriple
[0] = data
[i
++];
947 saveTriple
[1] = data
[i
];
950 saveTriple
[0] = data
[i
];
955 PString
PBase64::GetEncodedString()
957 PString retval
= encodedString
;
964 PString
PBase64::CompleteEncoding()
966 char * out
= encodedString
.GetPointer(encodeLength
+ 5)+encodeLength
;
970 *out
++ = Binary2Base64
[saveTriple
[0] >> 2];
971 *out
++ = Binary2Base64
[(saveTriple
[0]&3)<<4];
977 *out
++ = Binary2Base64
[saveTriple
[0] >> 2];
978 *out
++ = Binary2Base64
[((saveTriple
[0]&3)<<4) | (saveTriple
[1]>>4)];
979 *out
++ = Binary2Base64
[((saveTriple
[1]&15)<<2)];
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
)
1008 encoder
.ProcessEncoding(data
, length
);
1009 return encoder
.CompleteEncoding();
1013 void PBase64::StartDecoding()
1015 perfectDecode
= TRUE
;
1017 decodedData
.SetSize(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
1050 BYTE value
= Base642Binary
[(BYTE
)*cstr
++];
1052 case 96 : // end of string
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
1064 break; // Ignore totally
1066 case 99 : // Illegal characters
1067 perfectDecode
= FALSE
; // Ignore rubbish but flag decode as suspect
1070 default : // legal value from 0 to 63
1071 BYTE
* out
= decodedData
.GetPointer(((decodeSize
+1)&~255) + 256);
1072 switch (quadPosition
) {
1074 out
[decodeSize
] = (BYTE
)(value
<< 2);
1077 out
[decodeSize
++] |= (BYTE
)(value
>> 4);
1078 out
[decodeSize
] = (BYTE
)((value
&15) << 4);
1081 out
[decodeSize
++] |= (BYTE
)(value
>> 2);
1082 out
[decodeSize
] = (BYTE
)((value
&3) << 6);
1085 out
[decodeSize
++] |= (BYTE
)value
;
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);
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);
1117 PString
PBase64::Decode(const PString
& str
)
1121 return PString((const char *)(const BYTE
*)data
, data
.GetSize());
1125 BOOL
PBase64::Decode(const PString
& str
, PBYTEArray
& data
)
1128 decoder
.ProcessDecoding(str
);
1129 data
= decoder
.GetDecodedData();
1130 return decoder
.IsDecodeOK();
1134 BOOL
PBase64::Decode(const PString
& str
, void * dataBlock
, PINDEX length
)
1137 decoder
.ProcessDecoding(str
);
1138 return decoder
.GetDecodedData(dataBlock
, length
);
1142 // End Of File ///////////////////////////////////////////////////////////////