4 * Class for secure service applications using HTTPS as the user interface.
6 * Portable Windows Library
8 * Copyright (c) 2001-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.11 2004/04/24 03:58:15 rjongbloed
28 * Allow for run time enable/disable of secure web access to HTTP process,
29 * changed from old debug only hack to "correct" usager. Thanks Ben Lear
31 * Revision 1.10 2002/11/06 22:47:25 robertj
32 * Fixed header comment (copyright etc)
34 * Revision 1.9 2002/08/05 05:40:45 robertj
35 * Fixed missing pragma interface/implementation
37 * Revision 1.8 2001/12/13 09:19:32 robertj
38 * Added ability to create HTTP server certificate if one does not exist.
40 * Revision 1.7 2001/09/10 02:51:23 robertj
41 * Major change to fix problem with error codes being corrupted in a
42 * PChannel when have simultaneous reads and writes in threads.
44 * Revision 1.6 2001/08/28 06:44:45 craigs
45 * Added ability to override PHTTPServer creation
47 * Revision 1.5 2001/05/24 01:01:28 robertj
48 * Fixed GNU C++ warning
50 * Revision 1.4 2001/05/16 06:02:37 craigs
51 * Changed to allow detection of non-SSL connection to SecureHTTPServiceProcess
53 * Revision 1.3 2001/05/07 23:27:06 robertj
54 * Added SO_LINGER setting to HTTP sockets to help with clearing up sockets
55 * when the application exits, which prevents new run of app as "port in use".
57 * Revision 1.2 2001/03/27 03:55:48 craigs
58 * Added hack to allow secure servers to act as non-secure servers
60 * Revision 1.1 2001/02/15 02:41:14 robertj
61 * Added class to do secure HTTP based service process.
68 #pragma implementation "shttpsvc.h"
71 #include <ptclib/shttpsvc.h>
75 class HTTP_PSSLChannel
: public PSSLChannel
77 PCLASSINFO(HTTP_PSSLChannel
, PSSLChannel
);
80 HTTP_PSSLChannel(PSecureHTTPServiceProcess
* svc
, PSSLContext
* context
= NULL
);
82 virtual BOOL
RawSSLRead(void * buf
, PINDEX
& len
);
85 enum { PreRead_Size
= 4 };
87 PSecureHTTPServiceProcess
* svc
;
89 char preRead
[PreRead_Size
];
95 PSecureHTTPServiceProcess::PSecureHTTPServiceProcess(const Info
& inf
)
96 : PHTTPServiceProcess(inf
)
98 sslContext
= new PSSLContext
;
103 PSecureHTTPServiceProcess::~PSecureHTTPServiceProcess()
109 PHTTPServer
* PSecureHTTPServiceProcess::CreateHTTPServer(PTCPSocket
& socket
)
112 return PHTTPServiceProcess::CreateHTTPServer(socket
);
115 const linger ling
= { 1, 5 };
116 socket
.SetOption(SO_LINGER
, &ling
, sizeof(ling
));
119 PSSLChannel
* ssl
= new HTTP_PSSLChannel(this, sslContext
);
121 if (!ssl
->Accept(socket
)) {
122 PSYSTEMLOG(Error
, "HTTPS\tAccept failed: " << ssl
->GetErrorText());
127 PHTTPServer
* server
= OnCreateHTTPServer(httpNameSpace
);
129 server
->GetConnectionInfo().SetPersistenceMaximumTransations(0);
130 if (server
->Open(ssl
))
138 BOOL
PSecureHTTPServiceProcess::SetServerCertificate(const PFilePath
& certificateFile
,
142 if (create
&& !PFile::Exists(certificateFile
)) {
143 PSSLPrivateKey
key(1024);
144 PSSLCertificate certificate
;
149 name
<< "/O=" << GetManufacturer()
150 << "/CN=" << GetName() << '@' << PIPSocket::GetHostName();
152 if (!certificate
.CreateRoot(name
, key
)) {
153 PTRACE(0, "MTGW\tCould not create certificate");
156 certificate
.Save(certificateFile
);
157 key
.Save(certificateFile
, TRUE
);
160 return sslContext
->UseCertificate(certificateFile
) &&
161 sslContext
->UsePrivateKey(certificateFile
);
164 BOOL
PSecureHTTPServiceProcess::OnDetectedNonSSLConnection(PChannel
* chan
, const PString
& line
)
167 PMIMEInfo
mime(*chan
);
171 // get the host field
172 PString host
= mime("host");
173 if (!host
.IsEmpty()) {
176 PINDEX pos
= line
.Find(' ');
177 if (pos
!= P_MAX_INDEX
) {
178 PString str
= line
.Mid(pos
).Trim();
179 pos
= str
.FindLast(' ');
180 if (pos
!= P_MAX_INDEX
)
181 url
= host
+ str
.Left(pos
);
185 // no URL was available, return something!
190 PIPSocket::Address addr
;
191 PIPSocket::GetHostAddress(addr
);
192 url
= addr
.AsString() + ":" + PString(PString::Unsigned
, httpListeningSocket
->GetPort());
196 PString str
= CreateNonSSLMessage(PString("http://") + url
);
198 chan
->WriteString(str
);
204 PString
PSecureHTTPServiceProcess::CreateNonSSLMessage(const PString
& url
)
206 PString newUrl
= url
;
207 if (url
.Left(5) == "http:")
208 newUrl
= PString("https:") + newUrl
.Mid(5);
209 return CreateRedirectMessage(newUrl
);
212 PString
PSecureHTTPServiceProcess::CreateRedirectMessage(const PString
& url
)
214 return PString("HTTP/1.1 301 Moved Permanently\r\n") +
215 "Location: " + url
+ "\r\n" +
219 HTTP_PSSLChannel::HTTP_PSSLChannel(PSecureHTTPServiceProcess
* _svc
, PSSLContext
* context
)
220 : PSSLChannel(context
), svc(_svc
)
222 preReadLen
= P_MAX_INDEX
;
226 BOOL
HTTP_PSSLChannel::RawSSLRead(void * buf
, PINDEX
& len
)
229 return PSSLChannel::RawSSLRead(buf
, len
);
231 if (preReadLen
== P_MAX_INDEX
) {
232 PChannel
* chan
= GetReadChannel();
234 // read some bytes from the channel
236 while (preReadLen
< PreRead_Size
) {
237 BOOL b
= chan
->Read(preRead
+ preReadLen
, PreRead_Size
- preReadLen
);
240 preReadLen
+= chan
->GetLastReadCount();
243 // see if these bytes correspond to a GET or POST
245 (preReadLen
== PreRead_Size
) &&
246 ((strncmp(preRead
, "GET", 3) == 0) || (strncmp(preRead
, "POST", 4) == 0))
249 // read in the rest of the line
250 PString
line(preRead
, 4);
252 while (((ch
= chan
->ReadChar()) > 0) && (ch
!= '\n'))
255 if (!svc
->OnDetectedNonSSLConnection(chan
, line
))
260 // copy some bytes to the returned buffer, but no more than the buffer will allow
261 len
= PMIN(len
, preReadLen
);
262 memcpy(buf
, preRead
, len
);
269 // End Of File ///////////////////////////////////////////////////////////////