1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsTXTToHTMLConv.h"
39 : #include "nsNetUtil.h"
40 : #include "nsEscape.h"
41 : #include "nsStringStream.h"
42 : #include "nsAutoPtr.h"
43 :
44 : #define TOKEN_DELIMITERS NS_LITERAL_STRING("\t\r\n ").get()
45 :
46 : // nsISupports methods
47 0 : NS_IMPL_ISUPPORTS4(nsTXTToHTMLConv,
48 : nsIStreamConverter,
49 : nsITXTToHTMLConv,
50 : nsIRequestObserver,
51 : nsIStreamListener)
52 :
53 :
54 : // nsIStreamConverter methods
55 : NS_IMETHODIMP
56 0 : nsTXTToHTMLConv::Convert(nsIInputStream *aFromStream,
57 : const char *aFromType, const char *aToType,
58 : nsISupports *aCtxt, nsIInputStream * *_retval)
59 : {
60 0 : return NS_ERROR_NOT_IMPLEMENTED;
61 : }
62 :
63 : NS_IMETHODIMP
64 0 : nsTXTToHTMLConv::AsyncConvertData(const char *aFromType,
65 : const char *aToType,
66 : nsIStreamListener *aListener,
67 : nsISupports *aCtxt)
68 : {
69 0 : NS_ASSERTION(aListener, "null pointer");
70 0 : mListener = aListener;
71 0 : return NS_OK;
72 : }
73 :
74 :
75 : // nsIRequestObserver methods
76 : NS_IMETHODIMP
77 0 : nsTXTToHTMLConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
78 : {
79 0 : mBuffer.AssignLiteral("<html>\n<head><title>");
80 0 : mBuffer.Append(mPageTitle);
81 0 : mBuffer.AppendLiteral("</title></head>\n<body>\n");
82 0 : if (mPreFormatHTML) { // Use <pre> tags
83 0 : mBuffer.AppendLiteral("<pre>\n");
84 : }
85 :
86 : // Push mBuffer to the listener now, so the initial HTML will not
87 : // be parsed in OnDataAvailable().
88 :
89 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
90 0 : if (channel)
91 0 : channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
92 : // else, assume there is a channel somewhere that knows what it is doing!
93 :
94 0 : nsresult rv = mListener->OnStartRequest(request, aContext);
95 0 : if (NS_FAILED(rv)) return rv;
96 :
97 : // The request may have been canceled, and if that happens, we want to
98 : // suppress calls to OnDataAvailable.
99 0 : request->GetStatus(&rv);
100 0 : if (NS_FAILED(rv)) return rv;
101 :
102 0 : nsCOMPtr<nsIInputStream> inputData;
103 0 : rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer);
104 0 : if (NS_FAILED(rv)) return rv;
105 :
106 0 : rv = mListener->OnDataAvailable(request, aContext,
107 0 : inputData, 0, mBuffer.Length());
108 0 : if (NS_FAILED(rv)) return rv;
109 0 : mBuffer.Truncate();
110 0 : return rv;
111 : }
112 :
113 : NS_IMETHODIMP
114 0 : nsTXTToHTMLConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
115 : nsresult aStatus)
116 : {
117 0 : nsresult rv = NS_OK;
118 0 : if (mToken) {
119 : // we still have an outstanding token
120 0 : NS_ASSERTION(mToken->prepend,
121 : "Non prepending tokens should be handled in "
122 : "OnDataAvailable. There should only be a single "
123 : "prepending token left to be processed.");
124 0 : (void)CatHTML(0, mBuffer.Length());
125 : }
126 0 : if (mPreFormatHTML) {
127 0 : mBuffer.AppendLiteral("</pre>\n");
128 : }
129 0 : mBuffer.AppendLiteral("\n</body></html>");
130 :
131 0 : nsCOMPtr<nsIInputStream> inputData;
132 :
133 0 : rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer);
134 0 : if (NS_FAILED(rv)) return rv;
135 :
136 0 : rv = mListener->OnDataAvailable(request, aContext,
137 0 : inputData, 0, mBuffer.Length());
138 0 : if (NS_FAILED(rv)) return rv;
139 :
140 0 : return mListener->OnStopRequest(request, aContext, aStatus);
141 : }
142 :
143 : // nsITXTToHTMLConv methods
144 : NS_IMETHODIMP
145 0 : nsTXTToHTMLConv::SetTitle(const PRUnichar *aTitle)
146 : {
147 0 : mPageTitle.Assign(aTitle);
148 0 : return NS_OK;
149 : }
150 :
151 : NS_IMETHODIMP
152 0 : nsTXTToHTMLConv::PreFormatHTML(bool value)
153 : {
154 0 : mPreFormatHTML = value;
155 0 : return NS_OK;
156 : }
157 :
158 : // nsIStreamListener method
159 : NS_IMETHODIMP
160 0 : nsTXTToHTMLConv::OnDataAvailable(nsIRequest* request, nsISupports *aContext,
161 : nsIInputStream *aInStream,
162 : PRUint32 aOffset, PRUint32 aCount)
163 : {
164 0 : nsresult rv = NS_OK;
165 0 : nsString pushBuffer;
166 0 : PRUint32 amtRead = 0;
167 0 : nsAutoArrayPtr<char> buffer(new char[aCount+1]);
168 0 : if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
169 :
170 0 : do {
171 0 : PRUint32 read = 0;
172 : // XXX readSegments, to avoid the first copy?
173 0 : rv = aInStream->Read(buffer, aCount-amtRead, &read);
174 0 : if (NS_FAILED(rv)) return rv;
175 :
176 0 : buffer[read] = '\0';
177 : // XXX charsets?? non-latin1 characters?? utf-16??
178 0 : AppendASCIItoUTF16(buffer, mBuffer);
179 0 : amtRead += read;
180 :
181 0 : PRInt32 front = -1, back = -1, tokenLoc = -1, cursor = 0;
182 :
183 0 : while ( (tokenLoc = FindToken(cursor, &mToken)) > -1) {
184 0 : if (mToken->prepend) {
185 0 : front = mBuffer.RFindCharInSet(TOKEN_DELIMITERS, tokenLoc);
186 0 : front++;
187 0 : back = mBuffer.FindCharInSet(TOKEN_DELIMITERS, tokenLoc);
188 : } else {
189 0 : front = tokenLoc;
190 0 : back = front + mToken->token.Length();
191 : }
192 0 : if (back == -1) {
193 : // didn't find an ending, buffer up.
194 0 : mBuffer.Left(pushBuffer, front);
195 0 : cursor = front;
196 0 : break;
197 : }
198 : // found the end of the token.
199 0 : cursor = CatHTML(front, back);
200 : }
201 :
202 0 : PRInt32 end = mBuffer.RFind(TOKEN_DELIMITERS, mBuffer.Length());
203 0 : mBuffer.Left(pushBuffer, NS_MAX(cursor, end));
204 0 : mBuffer.Cut(0, NS_MAX(cursor, end));
205 0 : cursor = 0;
206 :
207 0 : if (!pushBuffer.IsEmpty()) {
208 0 : nsCOMPtr<nsIInputStream> inputData;
209 :
210 0 : rv = NS_NewStringInputStream(getter_AddRefs(inputData), pushBuffer);
211 0 : if (NS_FAILED(rv))
212 0 : return rv;
213 :
214 0 : rv = mListener->OnDataAvailable(request, aContext,
215 0 : inputData, 0, pushBuffer.Length());
216 0 : if (NS_FAILED(rv))
217 0 : return rv;
218 : }
219 : } while (amtRead < aCount);
220 :
221 0 : return rv;
222 : }
223 :
224 : // nsTXTToHTMLConv methods
225 0 : nsTXTToHTMLConv::nsTXTToHTMLConv()
226 : {
227 0 : mToken = nsnull;
228 0 : mPreFormatHTML = false;
229 0 : }
230 :
231 0 : nsTXTToHTMLConv::~nsTXTToHTMLConv()
232 : {
233 0 : mTokens.Clear();
234 0 : }
235 :
236 : nsresult
237 0 : nsTXTToHTMLConv::Init()
238 : {
239 0 : nsresult rv = NS_OK;
240 :
241 : // build up the list of tokens to handle
242 0 : convToken *token = new convToken;
243 0 : if (!token) return NS_ERROR_OUT_OF_MEMORY;
244 0 : token->prepend = false;
245 0 : token->token.Assign(PRUnichar('<'));
246 0 : token->modText.AssignLiteral("<");
247 0 : mTokens.AppendElement(token);
248 :
249 0 : token = new convToken;
250 0 : if (!token) return NS_ERROR_OUT_OF_MEMORY;
251 0 : token->prepend = false;
252 0 : token->token.Assign(PRUnichar('>'));
253 0 : token->modText.AssignLiteral(">");
254 0 : mTokens.AppendElement(token);
255 :
256 0 : token = new convToken;
257 0 : if (!token) return NS_ERROR_OUT_OF_MEMORY;
258 0 : token->prepend = false;
259 0 : token->token.Assign(PRUnichar('&'));
260 0 : token->modText.AssignLiteral("&");
261 0 : mTokens.AppendElement(token);
262 :
263 0 : token = new convToken;
264 0 : if (!token) return NS_ERROR_OUT_OF_MEMORY;
265 0 : token->prepend = true;
266 0 : token->token.AssignLiteral("http://"); // XXX need to iterate through all protos
267 0 : mTokens.AppendElement(token);
268 :
269 0 : token = new convToken;
270 0 : if (!token) return NS_ERROR_OUT_OF_MEMORY;
271 0 : token->prepend = true;
272 0 : token->token.Assign(PRUnichar('@'));
273 0 : token->modText.AssignLiteral("mailto:");
274 0 : mTokens.AppendElement(token);
275 :
276 0 : return rv;
277 : }
278 :
279 : PRInt32
280 0 : nsTXTToHTMLConv::FindToken(PRInt32 cursor, convToken* *_retval)
281 : {
282 0 : PRInt32 loc = -1, firstToken = mBuffer.Length();
283 0 : PRInt8 token = -1;
284 0 : for (PRUint8 i=0; i < mTokens.Length(); i++) {
285 0 : loc = mBuffer.Find(mTokens[i]->token, cursor);
286 0 : if (loc != -1)
287 0 : if (loc < firstToken) {
288 0 : firstToken = loc;
289 0 : token = i;
290 : }
291 : }
292 0 : if (token == -1)
293 0 : return -1;
294 :
295 0 : *_retval = mTokens[token];
296 0 : return firstToken;
297 : }
298 :
299 : PRInt32
300 0 : nsTXTToHTMLConv::CatHTML(PRInt32 front, PRInt32 back)
301 : {
302 0 : PRInt32 cursor = 0;
303 0 : PRInt32 modLen = mToken->modText.Length();
304 0 : if (!mToken->prepend) {
305 : // replace the entire token (from delimiter to delimiter)
306 0 : mBuffer.Cut(front, back - front);
307 0 : mBuffer.Insert(mToken->modText, front);
308 0 : cursor = front+modLen;
309 : } else {
310 0 : nsString linkText;
311 : // href is implied
312 0 : mBuffer.Mid(linkText, front, back-front);
313 :
314 0 : mBuffer.Insert(NS_LITERAL_STRING("<a href=\""), front);
315 0 : cursor += front+9;
316 0 : if (modLen) {
317 0 : mBuffer.Insert(mToken->modText, cursor);
318 0 : cursor += modLen;
319 : }
320 :
321 0 : NS_ConvertUTF16toUTF8 linkTextUTF8(linkText);
322 0 : nsCString escaped;
323 0 : if (NS_EscapeURL(linkTextUTF8.Data(), linkTextUTF8.Length(), esc_Minimal, escaped)) {
324 0 : mBuffer.Cut(cursor, back - front);
325 0 : CopyUTF8toUTF16(escaped, linkText);
326 0 : mBuffer.Insert(linkText, cursor);
327 0 : back = front + linkText.Length();
328 : }
329 :
330 0 : cursor += back-front;
331 0 : mBuffer.Insert(NS_LITERAL_STRING("\">"), cursor);
332 0 : cursor += 2;
333 0 : mBuffer.Insert(linkText, cursor);
334 0 : cursor += linkText.Length();
335 0 : mBuffer.Insert(NS_LITERAL_STRING("</a>"), cursor);
336 0 : cursor += 4;
337 : }
338 0 : mToken = nsnull; // indicates completeness
339 0 : return cursor;
340 : }
|