Back to index

lightning-sunbird  0.9+nobinonly
nsGopherChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Gopher protocol code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Bradley Baetz.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Bradley Baetz <bbaetz@student.usyd.edu.au>
00025  *   Darin Fisher <darin@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 // gopher implementation - based on datetime and finger implementations
00042 // and documentation
00043 
00044 #include "nsGopherChannel.h"
00045 #include "nsMimeTypes.h"
00046 #include "nsEscape.h"
00047 #include "nsNetUtil.h"
00048 #include "netCore.h"
00049 #include "nsCRT.h"
00050 #include "prlog.h"
00051 
00052 #include "nsIServiceManager.h"
00053 #include "nsILoadGroup.h"
00054 #include "nsIInterfaceRequestor.h"
00055 #include "nsIInterfaceRequestorUtils.h"
00056 #include "nsISocketTransportService.h"
00057 #include "nsIStringStream.h"
00058 #include "nsIStreamConverterService.h"
00059 #include "nsITXTToHTMLConv.h"
00060 #include "nsIEventQueue.h"
00061 #include "nsEventQueueUtils.h"
00062 #include "nsIPrefService.h"
00063 #include "nsIPrefBranch.h"
00064 
00065 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00066 static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
00067 
00068 #ifdef PR_LOGGING
00069 extern PRLogModuleInfo* gGopherLog;
00070 #endif
00071 
00072 #define BUFFER_SEG_SIZE (4*1024)
00073 #define BUFFER_MAX_SIZE (64*1024)
00074 
00075 // nsGopherChannel methods
00076 nsGopherChannel::nsGopherChannel()
00077     : mContentLength(-1),
00078       mType(-1),
00079       mStatus(NS_OK),
00080       mIsPending(PR_FALSE)
00081 {
00082 }
00083 
00084 nsGopherChannel::~nsGopherChannel()
00085 {
00086 #ifdef PR_LOGGING
00087     nsCAutoString spec;
00088     mUrl->GetAsciiSpec(spec);
00089     PR_LOG(gGopherLog, PR_LOG_ALWAYS, ("~nsGopherChannel() for %s", spec.get()));
00090 #endif
00091 }
00092 
00093 NS_IMPL_THREADSAFE_ISUPPORTS5(nsGopherChannel,
00094                               nsIChannel,
00095                               nsIRequest,
00096                               nsIStreamListener,
00097                               nsIRequestObserver,
00098                               nsITransportEventSink)
00099 
00100 nsresult
00101 nsGopherChannel::Init(nsIURI* uri, nsIProxyInfo* proxyInfo)
00102 {
00103     nsresult rv;
00104 
00105     nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
00106     if (NS_FAILED(rv))
00107         return NS_ERROR_MALFORMED_URI;
00108     
00109     mUrl = uri;
00110     mProxyInfo = proxyInfo;
00111     
00112     nsCAutoString buffer;
00113 
00114     rv = url->GetPath(buffer); // unescaped down below
00115     if (NS_FAILED(rv))
00116         return rv;
00117 
00118     rv = url->GetAsciiHost(mHost);
00119     if (NS_FAILED(rv))
00120         return rv;
00121     rv = url->GetPort(&mPort);
00122     if (NS_FAILED(rv))
00123         return rv;
00124 
00125     // For security reasons, don't allow anything expect the default
00126     // gopher port (70). See bug 71916 - bbaetz@cs.mcgill.ca
00127 /*
00128     if (mPort==-1)
00129         mPort=GOPHER_PORT;
00130 */
00131     mPort=GOPHER_PORT;
00132 
00133     // No path given
00134     if (buffer[0]=='\0' || (buffer[0]=='/' && buffer[1]=='\0')) {
00135         mType = '1';
00136         mSelector.Truncate();
00137     } else {
00138         mType = buffer[1]; // Ignore leading '/'
00139 
00140         // Do it this way in case selector contains embedded nulls after
00141         // unescaping
00142         char* selector = nsCRT::strdup(buffer.get()+2);
00143         PRInt32 count = nsUnescapeCount(selector);
00144         mSelector.Assign(selector,count);
00145         nsCRT::free(selector);
00146 
00147         if (mSelector.FindCharInSet(nsCString("\t\n\r\0",4)) != -1) {
00148             // gopher selectors cannot containt tab, cr, lf, or \0
00149             return NS_ERROR_MALFORMED_URI;
00150         }
00151     }
00152 
00153     PR_LOG(gGopherLog,
00154            PR_LOG_DEBUG,
00155            ("Host: mHost = %s, mPort = %d\n", mHost.get(), mPort));
00156     PR_LOG(gGopherLog,
00157            PR_LOG_DEBUG,
00158            ("Status: mType = %c, mSelector = %s\n", mType, mSelector.get()));
00159     
00160     return NS_OK;
00161 }
00162 
00163 
00165 // nsIRequest methods:
00166 
00167 NS_IMETHODIMP
00168 nsGopherChannel::GetName(nsACString &result)
00169 {
00170     return mUrl->GetHostPort(result);
00171 }
00172 
00173 NS_IMETHODIMP
00174 nsGopherChannel::IsPending(PRBool *result)
00175 {
00176     *result = mIsPending;
00177     return NS_OK;
00178 }
00179 
00180 NS_IMETHODIMP
00181 nsGopherChannel::GetStatus(nsresult *status)
00182 {
00183     if (mPump && NS_SUCCEEDED(mStatus))
00184         mPump->GetStatus(status);
00185     else
00186         *status = mStatus;
00187     return NS_OK;
00188 }
00189 
00190 NS_IMETHODIMP
00191 nsGopherChannel::Cancel(nsresult status)
00192 {
00193     PR_LOG(gGopherLog,
00194            PR_LOG_DEBUG,
00195            ("nsGopherChannel::Cancel() called [this=%x, status=%x]\n",
00196             this,status));
00197     
00198     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00199  
00200     mStatus = status;
00201     if (mPump)
00202         return mPump->Cancel(status);
00203 
00204     return NS_OK;
00205 }
00206 
00207 NS_IMETHODIMP
00208 nsGopherChannel::Suspend()
00209 {
00210     if (mPump)
00211         return mPump->Suspend();
00212 
00213     return NS_OK;
00214 }
00215 
00216 NS_IMETHODIMP
00217 nsGopherChannel::Resume()
00218 {
00219     if (mPump)
00220         return mPump->Resume();
00221 
00222     return NS_OK;
00223 }
00224 
00226 // nsIChannel methods:
00227 
00228 NS_IMETHODIMP
00229 nsGopherChannel::GetOriginalURI(nsIURI* *aURI)
00230 {
00231     *aURI = mOriginalURI ? mOriginalURI : mUrl;
00232     NS_ADDREF(*aURI);
00233     return NS_OK;
00234 }
00235 
00236 NS_IMETHODIMP
00237 nsGopherChannel::SetOriginalURI(nsIURI* aURI)
00238 {
00239     mOriginalURI = aURI;
00240     return NS_OK;
00241 }
00242 
00243 NS_IMETHODIMP
00244 nsGopherChannel::GetURI(nsIURI* *aURI)
00245 {
00246     *aURI = mUrl;
00247     NS_IF_ADDREF(*aURI);
00248     return NS_OK;
00249 }
00250 
00251 NS_IMETHODIMP
00252 nsGopherChannel::Open(nsIInputStream **_retval)
00253 {
00254     return NS_ImplementChannelOpen(this, _retval);
00255 }
00256 
00257 NS_IMETHODIMP
00258 nsGopherChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
00259 {
00260     PR_LOG(gGopherLog, PR_LOG_DEBUG, ("nsGopherChannel::AsyncOpen() called [this=%x]\n",
00261                                       this));
00262 
00263     // get callback interfaces...
00264 
00265     nsresult rv;
00266 
00267     PRInt32 port;
00268     rv = mUrl->GetPort(&port);
00269     if (NS_FAILED(rv))
00270         return rv;
00271  
00272     rv = NS_CheckPortSafety(port, "gopher");
00273     if (NS_FAILED(rv))
00274         return rv;
00275 
00276     // push stream converters in front of the consumer...
00277     nsCOMPtr<nsIStreamListener> converter;
00278     rv = PushStreamConverters(aListener, getter_AddRefs(converter));
00279     if (NS_FAILED(rv)) return rv;
00280     
00281     // create socket transport
00282     nsCOMPtr<nsISocketTransportService> socketService = 
00283              do_GetService(kSocketTransportServiceCID, &rv);
00284     if (NS_FAILED(rv)) return rv;
00285 
00286     rv = socketService->CreateTransport(nsnull, 0,
00287                                         mHost,
00288                                         mPort,
00289                                         mProxyInfo,
00290                                         getter_AddRefs(mTransport));
00291     if (NS_FAILED(rv)) return rv;
00292 
00293     // setup notification callbacks...
00294     if (!(mLoadFlags & LOAD_BACKGROUND)) {
00295         nsCOMPtr<nsIEventQueue> eventQ;
00296         NS_GetCurrentEventQ(getter_AddRefs(eventQ));
00297         if (eventQ)
00298             mTransport->SetEventSink(this, eventQ);
00299     }
00300 
00301     // open buffered, asynchronous socket input stream, and use a input stream
00302     // pump to read from it.
00303     nsCOMPtr<nsIInputStream> input;
00304     rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(input));
00305     if (NS_FAILED(rv)) return rv;
00306 
00307     rv = SendRequest();
00308     if (NS_FAILED(rv)) return rv;
00309 
00310     rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input);
00311     if (NS_FAILED(rv)) return rv;
00312 
00313     rv = mPump->AsyncRead(this, nsnull);
00314     if (NS_FAILED(rv)) return rv;
00315 
00316     if (mLoadGroup)
00317         mLoadGroup->AddRequest(this, nsnull);
00318 
00319     mIsPending = PR_TRUE;
00320     if (converter)
00321         mListener = converter;
00322     else
00323         mListener = aListener;
00324     mListenerContext = ctxt;
00325     return NS_OK;
00326 }
00327 
00328 NS_IMETHODIMP
00329 nsGopherChannel::GetLoadFlags(PRUint32 *aLoadFlags)
00330 {
00331     *aLoadFlags = mLoadFlags;
00332     return NS_OK;
00333 }
00334 
00335 NS_IMETHODIMP
00336 nsGopherChannel::SetLoadFlags(PRUint32 aLoadFlags)
00337 {
00338     mLoadFlags = aLoadFlags;
00339     return NS_OK;
00340 }
00341 
00342 NS_IMETHODIMP
00343 nsGopherChannel::GetContentType(nsACString &aContentType)
00344 {
00345     if (!mContentType.IsEmpty()) {
00346         aContentType = mContentType;
00347         return NS_OK;
00348     }
00349 
00350     switch(mType) {
00351     case '0':
00352     case 'h':
00353         aContentType.AssignLiteral(TEXT_HTML);
00354         break;
00355     case '1':
00356         aContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
00357         break;
00358     case '2': // CSO search - unhandled, should not be selectable
00359         aContentType.AssignLiteral(TEXT_HTML);
00360         break;
00361     case '3': // "Error" - should not be selectable
00362         aContentType.AssignLiteral(TEXT_HTML);
00363         break;
00364     case '4': // "BinHexed Macintosh file"
00365         aContentType.AssignLiteral(APPLICATION_BINHEX);
00366         break;
00367     case '5':
00368         // "DOS binary archive of some sort" - is the mime-type correct?
00369         aContentType.AssignLiteral(APPLICATION_OCTET_STREAM);
00370         break;
00371     case '6':
00372         aContentType.AssignLiteral(APPLICATION_UUENCODE);
00373         break;
00374     case '7': // search - returns a directory listing
00375         aContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
00376         break;
00377     case '8': // telnet - type doesn't make sense
00378         aContentType.AssignLiteral(TEXT_PLAIN);
00379         break;
00380     case '9': // "Binary file!"
00381         aContentType.AssignLiteral(APPLICATION_OCTET_STREAM);
00382         break;
00383     case 'g':
00384         aContentType.AssignLiteral(IMAGE_GIF);
00385         break;
00386     case 'i': // info line- should not be selectable
00387         aContentType.AssignLiteral(TEXT_HTML);
00388         break;
00389     case 'I':
00390         aContentType.AssignLiteral(IMAGE_GIF);
00391         break;
00392     case 'T': // tn3270 - type doesn't make sense
00393         aContentType.AssignLiteral(TEXT_PLAIN);
00394         break;
00395     default:
00396         if (!mContentTypeHint.IsEmpty()) {
00397             aContentType = mContentTypeHint;
00398         } else {
00399             NS_WARNING("Unknown gopher type");
00400             aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
00401         }
00402         break;
00403     }
00404 
00405     PR_LOG(gGopherLog,PR_LOG_DEBUG,
00406            ("GetContentType returning %s\n", PromiseFlatCString(aContentType).get()));
00407 
00408     // XXX do we want to cache this result?
00409     return NS_OK;
00410 }
00411 
00412 NS_IMETHODIMP
00413 nsGopherChannel::SetContentType(const nsACString &aContentType)
00414 {
00415     // If AsyncOpen has been called, then treat this value as a content-type
00416     // override.  Otherwise, treat it as a content-type hint.
00417     //
00418     // In the case in which we are being given a content-type hint, we have no
00419     // ways of determining a charset on our own, so just set mContentCharset
00420     // from the charset part of this.
00421 
00422     nsCString *contentType = mIsPending ? &mContentType : &mContentTypeHint;
00423     // Not in libnecko, so can't use net_ParseContentType
00424     NS_ParseContentType(aContentType, *contentType, mContentCharset);
00425     return NS_OK;
00426 }
00427 
00428 NS_IMETHODIMP
00429 nsGopherChannel::GetContentCharset(nsACString &aContentCharset)
00430 {
00431     aContentCharset = mContentCharset;
00432     return NS_OK;
00433 }
00434 
00435 NS_IMETHODIMP
00436 nsGopherChannel::SetContentCharset(const nsACString &aContentCharset)
00437 {
00438     // If someone gives us a charset hint we should just use that charset.
00439     // So we don't care when this is being called.
00440     mContentCharset = aContentCharset;
00441     return NS_OK;
00442 }
00443 
00444 NS_IMETHODIMP
00445 nsGopherChannel::GetContentLength(PRInt32 *aContentLength)
00446 {
00447     *aContentLength = mContentLength;
00448     return NS_OK;
00449 }
00450 
00451 NS_IMETHODIMP
00452 nsGopherChannel::SetContentLength(PRInt32 aContentLength)
00453 {
00454     NS_NOTREACHED("nsGopherChannel::SetContentLength");
00455     return NS_ERROR_NOT_IMPLEMENTED;
00456 }
00457 
00458 NS_IMETHODIMP
00459 nsGopherChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00460 {
00461     *aLoadGroup = mLoadGroup;
00462     NS_IF_ADDREF(*aLoadGroup);
00463     return NS_OK;
00464 }
00465 
00466 NS_IMETHODIMP
00467 nsGopherChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00468 {
00469     mLoadGroup = aLoadGroup;
00470     mProgressSink = nsnull;
00471     return NS_OK;
00472 }
00473 
00474 NS_IMETHODIMP
00475 nsGopherChannel::GetOwner(nsISupports* *aOwner)
00476 {
00477     *aOwner = mOwner.get();
00478     NS_IF_ADDREF(*aOwner);
00479     return NS_OK;
00480 }
00481 
00482 NS_IMETHODIMP
00483 nsGopherChannel::SetOwner(nsISupports* aOwner)
00484 {
00485     mOwner = aOwner;
00486     return NS_OK;
00487 }
00488 
00489 NS_IMETHODIMP
00490 nsGopherChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
00491 {
00492     NS_IF_ADDREF(*aCallbacks = mCallbacks);
00493     return NS_OK;
00494 }
00495 
00496 NS_IMETHODIMP
00497 nsGopherChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
00498 {
00499     mCallbacks = aCallbacks;
00500     mProgressSink = nsnull;
00501     return NS_OK;
00502 }
00503 
00504 NS_IMETHODIMP 
00505 nsGopherChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00506 {
00507     if (mTransport)
00508         return mTransport->GetSecurityInfo(aSecurityInfo);
00509 
00510     return NS_ERROR_NOT_AVAILABLE;
00511 }
00512 
00513 // nsIRequestObserver methods
00514 NS_IMETHODIMP
00515 nsGopherChannel::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
00516 {
00517     PR_LOG(gGopherLog,
00518            PR_LOG_DEBUG,
00519            ("nsGopherChannel::OnStartRequest called [this=%x, aRequest=%x]\n",
00520             this, aRequest));
00521 
00522     return mListener->OnStartRequest(this, mListenerContext);
00523 }
00524 
00525 NS_IMETHODIMP
00526 nsGopherChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
00527                                nsresult aStatus)
00528 {
00529     PR_LOG(gGopherLog,
00530            PR_LOG_DEBUG,
00531            ("nsGopherChannel::OnStopRequest called [this=%x, aRequest=%x, aStatus=%x]\n",
00532             this,aRequest,aStatus));
00533 
00534     if (NS_SUCCEEDED(mStatus))
00535         mStatus = aStatus;
00536 
00537     if (mListener) {
00538         mListener->OnStopRequest(this, mListenerContext, mStatus);
00539         mListener = 0;
00540         mListenerContext = 0;
00541     }
00542 
00543     if (mLoadGroup)
00544         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00545     
00546     mTransport->Close(mStatus);
00547     mTransport = 0;
00548     mPump = 0;
00549 
00550     // Drop notification callbacks to prevent cycles.
00551     mCallbacks = 0;
00552     mProgressSink = 0;
00553 
00554     return NS_OK;
00555 }
00556 
00557 
00558 // nsIStreamListener method
00559 NS_IMETHODIMP
00560 nsGopherChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
00561                                  nsIInputStream *stream,
00562                                  PRUint32 offset, PRUint32 count)
00563 {
00564     PR_LOG(gGopherLog, PR_LOG_DEBUG,
00565            ("OnDataAvailable called - [this=%x, aLength=%d]\n",this,count));
00566 
00567     return mListener->OnDataAvailable(this, mListenerContext, stream,
00568                                       offset, count);
00569 }
00570 
00571 nsresult
00572 nsGopherChannel::SendRequest()
00573 {
00574     nsresult rv = NS_OK;
00575 
00576     // Note - you have to keep this as a class member, because the char input
00577     // stream doesn't copy its buffer
00578     mRequest.Assign(mSelector);
00579 
00580     // So, we use the selector as is unless it is a search url
00581     if (mType=='7') {
00582         // Note that we don't use the "standard" nsIURL parsing stuff here
00583         // because the only special character is ?, and its possible to search
00584         // for a string containing a #, and so on
00585         
00586         // XXX - should this find the last or first entry?
00587         // '?' is valid in both the search string and the url
00588         // so no matter what this does, it may be incorrect
00589         // This only affects people codeing the query directly into the URL
00590         PRInt32 pos = mRequest.RFindChar('?');
00591         if (pos == -1) {
00592             // We require a query string here - if we don't have one,
00593             // then we need to ask the user
00594             nsCOMPtr<nsIPrompt> prompter;
00595             NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, prompter);
00596             if (!prompter) {
00597                 NS_ERROR("We need a prompter!");
00598                 return NS_ERROR_FAILURE;
00599             }
00600 
00601             if (!mStringBundle) {
00602 
00603                 nsCOMPtr<nsIStringBundleService> bundleSvc =
00604                         do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00605                 if (NS_FAILED(rv)) return rv;
00606 
00607                 rv = bundleSvc->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mStringBundle));
00608                 if (NS_FAILED(rv)) return rv;
00609 
00610             }
00611 
00612             nsXPIDLString  promptTitle;
00613             nsXPIDLString  promptText;
00614 
00615             if (mStringBundle)
00616                 rv = mStringBundle->GetStringFromName(NS_LITERAL_STRING("GopherPromptTitle").get(),
00617                                                       getter_Copies(promptTitle));
00618 
00619             if (NS_FAILED(rv) || !mStringBundle)
00620                 promptTitle.AssignLiteral("Search");
00621 
00622 
00623             if (mStringBundle)
00624                 rv = mStringBundle->GetStringFromName(NS_LITERAL_STRING("GopherPromptText").get(),
00625                                                       getter_Copies(promptText));
00626 
00627             if (NS_FAILED(rv) || !mStringBundle)
00628                 promptText.AssignLiteral("Enter a search term:");
00629 
00630 
00631             nsXPIDLString search;
00632             PRBool res;
00633             prompter->Prompt(promptTitle.get(),
00634                               promptText.get(),
00635                               getter_Copies(search),
00636                               NULL,
00637                               NULL,
00638                               &res);
00639             if (!res || !(*search.get()))
00640                 return NS_ERROR_FAILURE;
00641     
00642             mRequest.Append('\t');
00643             AppendUTF16toUTF8(search, mRequest); // XXX Is UTF-8 the right thing?
00644 
00645             // and update our uri
00646             nsCAutoString spec;
00647             rv = mUrl->GetAsciiSpec(spec);
00648             if (NS_FAILED(rv))
00649                 return rv;
00650 
00651             spec.Append('?');
00652             AppendUTF16toUTF8(search, spec);
00653             rv = mUrl->SetSpec(spec);
00654             if (NS_FAILED(rv))
00655                 return rv;
00656         } else {
00657             // Just replace it with a tab
00658             mRequest.SetCharAt('\t',pos);
00659         }
00660     }
00661 
00662     mRequest.Append(CRLF);
00663     
00664     PR_LOG(gGopherLog,PR_LOG_DEBUG,
00665            ("Sending: %s\n", mRequest.get()));
00666 
00667     // open a buffered, blocking output stream.  (it should never block because
00668     // the buffer is big enough for our entire request.)
00669     nsCOMPtr<nsIOutputStream> output;
00670     rv = mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
00671                                       mRequest.Length(), 1,
00672                                       getter_AddRefs(output));
00673     if (NS_FAILED(rv)) return rv;
00674 
00675     PRUint32 n;
00676     rv = output->Write(mRequest.get(), mRequest.Length(), &n);
00677     if (NS_FAILED(rv)) return rv;
00678 
00679     if (n != mRequest.Length())
00680         return NS_ERROR_UNEXPECTED;
00681 
00682     return NS_OK;
00683 }
00684 
00685 nsresult
00686 nsGopherChannel::PushStreamConverters(nsIStreamListener *listener, nsIStreamListener **result)
00687 {
00688     nsresult rv;
00689     nsCOMPtr<nsIStreamListener> converterListener;
00690     
00691     nsCOMPtr<nsIStreamConverterService> StreamConvService = 
00692              do_GetService(kStreamConverterServiceCID, &rv);
00693     if (NS_FAILED(rv)) return rv;
00694  
00695     // What we now do depends on what type of file we have
00696     if (mType=='1' || mType=='7') {
00697         // Send the directory format back for a directory
00698         rv = StreamConvService->AsyncConvertData("text/gopher-dir", 
00699                APPLICATION_HTTP_INDEX_FORMAT,
00700                listener,
00701                mUrl,
00702                getter_AddRefs(converterListener));
00703         if (NS_FAILED(rv)) return rv;
00704     } else if (mType=='0') {
00705         // Convert general file
00706         rv = StreamConvService->AsyncConvertData("text/plain",
00707                                                  "text/html",
00708                                                  listener,
00709                                                  mListenerContext,
00710                                                  getter_AddRefs(converterListener));
00711         if (NS_FAILED(rv)) return rv;
00712         
00713         nsCOMPtr<nsITXTToHTMLConv> converter(do_QueryInterface(converterListener));
00714         if (converter) {
00715             nsCAutoString spec;
00716             rv = mUrl->GetSpec(spec);
00717             converter->SetTitle(NS_ConvertUTF8toUCS2(spec).get());
00718             converter->PreFormatHTML(PR_TRUE);
00719         }
00720     }
00721 
00722     NS_IF_ADDREF(*result = converterListener);
00723     return NS_OK;
00724 }
00725 
00726 NS_IMETHODIMP
00727 nsGopherChannel::OnTransportStatus(nsITransport *trans, nsresult status,
00728                                    PRUint64 progress, PRUint64 progressMax)
00729 {
00730     if (!mProgressSink)
00731         NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
00732 
00733     // suppress status notification if channel is no longer pending!
00734     if (mProgressSink && NS_SUCCEEDED(mStatus) && mPump && !(mLoadFlags & LOAD_BACKGROUND)) {
00735         NS_ConvertUTF8toUTF16 host(mHost);
00736         mProgressSink->OnStatus(this, nsnull, status, host.get());
00737 
00738         if (status == nsISocketTransport::STATUS_RECEIVING_FROM ||
00739             status == nsISocketTransport::STATUS_SENDING_TO) {
00740             mProgressSink->OnProgress(this, nsnull, progress, progressMax);
00741         }
00742     }
00743     return NS_OK;
00744 }