/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is XMLterm. * * The Initial Developer of the Original Code is * Ramalingam Saravanan. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // mozLineTerm.cpp: class implementing mozILineTerm/mozILineTermAux interfaces, // providing an XPCOM/XPCONNECT wrapper for the LINETERM module #include #include "nspr.h" #include "nscore.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsXPIDLString.h" #include "plstr.h" #include "nsReadableUtils.h" #include "prlog.h" #include "nsMemory.h" #include "nsIServiceManager.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIPrincipal.h" #include "nsIDocument.h" #include "nsIDOMHTMLDocument.h" #include "mozXMLT.h" #include "mozXMLTermUtils.h" #include "mozLineTerm.h" #define MAXCOL 4096 // Maximum columns in line buffer ///////////////////////////////////////////////////////////////////////// // mozLineTerm implementaion ///////////////////////////////////////////////////////////////////////// NS_GENERIC_FACTORY_CONSTRUCTOR(mozLineTerm) NS_IMPL_THREADSAFE_ISUPPORTS2(mozLineTerm, mozILineTerm, mozILineTermAux) PRBool mozLineTerm::mLoggingEnabled = PR_FALSE; PRBool mozLineTerm::mLoggingInitialized = PR_FALSE; NS_METHOD mozLineTerm::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { if (!mLoggingInitialized) { // Initialize all LINETERM operations // (This initialization needs to be done at factory creation time; // trying to do it earlier, i.e., at registration time, // does not work ... something to do with loading of static global // variables.) int messageLevel = 0; char* debugStr = (char*) PR_GetEnv("LTERM_DEBUG"); if (debugStr && (strlen(debugStr) == 1)) { messageLevel = 98; debugStr = nsnull; } int result = lterm_init(0); if (result == 0) { tlog_set_level(LTERM_TLOG_MODULE, messageLevel, debugStr); } mLoggingInitialized = PR_TRUE; char* logStr = (char*) PR_GetEnv("LTERM_LOG"); if (logStr && *logStr) { // Enable LineTerm logging mozLineTerm::mLoggingEnabled = PR_TRUE; } } return mozLineTermConstructor( aOuter, aIID, aResult ); } mozLineTerm::mozLineTerm() : mCursorRow(0), mCursorColumn(0), mSuspended(PR_FALSE), mEchoFlag(PR_TRUE), mObserver(nsnull), mCookie(EmptyString()), mLastTime(LL_ZERO) { mLTerm = lterm_new(); } mozLineTerm::~mozLineTerm() { lterm_delete(mLTerm); mObserver = nsnull; } /** Checks if preference settings are secure for LineTerm creation and use */ NS_IMETHODIMP mozLineTerm::ArePrefsSecure(PRBool *_retval) { nsresult result; XMLT_LOG(mozLineTerm::ArePrefsSecure,30,("\n")); if (!_retval) return NS_ERROR_FAILURE; *_retval = PR_FALSE; nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); if (!prefBranch) return NS_ERROR_FAILURE; // Check if Components JS object is secure PRBool checkXPC; result = prefBranch->GetBoolPref("security.checkxpconnect", &checkXPC); if (NS_FAILED(result)) return NS_ERROR_FAILURE; if (!checkXPC) { XMLT_ERROR("mozLineTerm::ArePrefsSecure: Error - Please add the line\n" " pref(\"security.checkxpcconnect\",true);\n" "to your preferences file (.mozilla/prefs.js)\n"); *_retval = PR_FALSE; #if 0 // Temporarily comented out return NS_OK; #endif } nsCAutoString secString ("security.policy."); /* Get global policy name. */ nsXPIDLCString policyStr; result = prefBranch->GetCharPref("javascript.security_policy", getter_Copies(policyStr)); if (NS_SUCCEEDED(result) && !policyStr.IsEmpty()) { secString.Append(policyStr); } else { secString.Append("default"); } secString.Append(".htmldocument.cookie"); XMLT_LOG(mozLineTerm::ArePrefsSecure,32, ("prefStr=%s\n", secString.get())); nsXPIDLCString secLevelString; result = prefBranch->GetCharPref(secString.get(), getter_Copies(secLevelString)); if (NS_FAILED(result)) return NS_ERROR_FAILURE; XMLT_LOG(mozLineTerm::ArePrefsSecure,32, ("secLevelString=%s\n", secLevelString.get())); *_retval = secLevelString.EqualsLiteral("sameOrigin"); if (!(*_retval)) { XMLT_ERROR("mozLineTerm::ArePrefsSecure: Error - Please add the line\n" " pref(\"security.policy.default.htmldocument.cookie\",\"sameOrigin\");\n" "to your preferences file (.mozilla/prefs.js)\n"); } return NS_OK; } /** Checks document principal to ensure it has LineTerm creation privileges. * Returns the principal string if the principal is secure, * and a (zero length) null string if the principal is insecure. */ NS_IMETHODIMP mozLineTerm::GetSecurePrincipal(nsIDOMDocument *domDoc, char** aPrincipalStr) { XMLT_LOG(mozLineTerm::GetSecurePrincipal,30,("\n")); if (!aPrincipalStr) return NS_ERROR_FAILURE; *aPrincipalStr = nsnull; // Get principal string nsCOMPtr doc = do_QueryInterface(domDoc); if (!doc) return NS_ERROR_FAILURE; nsIPrincipal *principal = doc->GetPrincipal(); if (!principal) return NS_ERROR_FAILURE; #if 0 // Temporarily commented out, because |ToString()| is not implemented. if (NS_FAILED(principal->ToString(aPrincipalStr)) || !*aPrincipalStr) return NS_ERROR_FAILURE; #else const char temStr[] = "unknown"; PRInt32 temLen = strlen(temStr); *aPrincipalStr = strncpy((char*) nsMemory::Alloc(temLen+1), temStr, temLen+1); #endif XMLT_LOG(mozLineTerm::GetSecurePrincipal,32,("aPrincipalStr=%s\n", *aPrincipalStr)); // Check if principal is secure PRBool insecure = PR_FALSE; if (insecure) { // Return null string XMLT_ERROR("mozLineTerm::GetSecurePrincipal: Error - " "Insecure document principal %s\n", *aPrincipalStr); nsMemory::Free(*aPrincipalStr); *aPrincipalStr = (char*) nsMemory::Alloc(1); **aPrincipalStr = '\0'; } return NS_OK; } /** Open LineTerm without callback */ NS_IMETHODIMP mozLineTerm::Open(const PRUnichar *command, const PRUnichar *initInput, const PRUnichar *promptRegexp, PRInt32 options, PRInt32 processType, nsIDOMDocument *domDoc) { if (mSuspended) { XMLT_ERROR("mozLineTerm::Open: Error - LineTerm %d is suspended\n", mLTerm); return NS_ERROR_FAILURE; } nsAutoString aCookie; return OpenAux(command, initInput, promptRegexp, options, processType, 24, 80, 0, 0, domDoc, nsnull, aCookie); } /** Open LineTerm, with an Observer for callback to process new input/output */ NS_IMETHODIMP mozLineTerm::OpenAux(const PRUnichar *command, const PRUnichar *initInput, const PRUnichar *promptRegexp, PRInt32 options, PRInt32 processType, PRInt32 nRows, PRInt32 nCols, PRInt32 xPixels, PRInt32 yPixels, nsIDOMDocument *domDoc, nsIObserver* anObserver, nsString& aCookie) { nsresult result; XMLT_LOG(mozLineTerm::Open,20,("\n")); // Ensure that preferences are secure for LineTerm creation and use PRBool arePrefsSecure; result = ArePrefsSecure(&arePrefsSecure); #if 0 // Temporarily comented out if (NS_FAILED(result) || !arePrefsSecure) return NS_ERROR_FAILURE; #endif // Ensure that document principal is secure for LineTerm creation char* securePrincipal; result = GetSecurePrincipal(domDoc, &securePrincipal); if (NS_FAILED(result)) return NS_ERROR_FAILURE; if (!*securePrincipal) { nsMemory::Free(securePrincipal); XMLT_ERROR("mozLineTerm::OpenAux: Error - " "Failed to create LineTerm for insecure document principal\n"); return NS_ERROR_FAILURE; } nsCOMPtr domHTMLDoc = do_QueryInterface(domDoc); if (!domHTMLDoc) return NS_ERROR_FAILURE; // Ensure that cookie attribute of document is defined NS_NAMED_LITERAL_STRING(cookiePrefix, "xmlterm="); nsAutoString cookieStr; result = domHTMLDoc->GetCookie(cookieStr); if (NS_SUCCEEDED(result) && (cookieStr.Length() > cookiePrefix.Length()) && StringBeginsWith(cookieStr, cookiePrefix)) { // Cookie value already defined for document; simply copy it mCookie = cookieStr; } else { // Create random session cookie nsAutoString cookieValue; result = mozXMLTermUtils::RandomCookie(cookieValue); if (NS_FAILED(result)) return result; mCookie = cookiePrefix; mCookie += cookieValue; // Set new cookie value result = domHTMLDoc->SetCookie(mCookie); if (NS_FAILED(result)) return result; } // Return copy of cookie to caller aCookie = mCookie; mObserver = anObserver; // non-owning reference // Convert cookie to CString char* cookieCStr = ToNewCString(mCookie); XMLT_LOG(mozLineTerm::Open,22, ("mCookie=%s\n", cookieCStr)); // Convert initInput to CString NS_LossyConvertUTF16toASCII initCStr(initInput); // XXX ASCII? XMLT_LOG(mozLineTerm::Open,22, ("initInput=%s\n", initCStr.get())); // List of prompt delimiters static const PRInt32 PROMPT_DELIMS = 5; UNICHAR prompt_regexp[PROMPT_DELIMS+1]; ucscopy(prompt_regexp, "#$%>?", PROMPT_DELIMS+1); PR_ASSERT(ucslen(prompt_regexp) == PROMPT_DELIMS); if (anObserver != nsnull) { result = lterm_open(mLTerm, NULL, cookieCStr, initCStr.get(), prompt_regexp, options, processType, nRows, nCols, xPixels, yPixels, mozLineTerm::Callback, (void *) this); } else { result = lterm_open(mLTerm, NULL, cookieCStr, initCStr.get(), prompt_regexp, options, processType, nRows, nCols, xPixels, yPixels, NULL, NULL); } // Free cookie CString nsMemory::Free(cookieCStr); if (mLoggingEnabled) { // Log time stamp for LineTerm open operation nsAutoString timeStamp; result = mozXMLTermUtils::TimeStamp(0, mLastTime, timeStamp); if (NS_SUCCEEDED(result)) { char* temStr = ToNewCString(timeStamp); PR_LogPrint(" LineTerm %d opened by principal %s\n", temStr, mLTerm, securePrincipal); nsMemory::Free(temStr); } } if (result == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; } } /** GTK-style callback funtion for mozLineTerm object */ void mozLineTerm::Callback(gpointer data, gint source, GdkInputCondition condition) { mozLineTerm* lineTerm = (mozLineTerm*) data; //XMLT_LOG(mozLineTerm::Callback,50,("\n")); PR_ASSERT(lineTerm != nsnull); PR_ASSERT(lineTerm->mObserver != nsnull); lineTerm->mObserver->Observe((nsISupports*) lineTerm, nsnull, nsnull); return; } /** Suspends (or restores) LineTerm activity depending upon aSuspend */ NS_IMETHODIMP mozLineTerm::SuspendAux(PRBool aSuspend) { mSuspended = aSuspend; return NS_OK; } /** Close LineTerm (a Finalize method) */ NS_IMETHODIMP mozLineTerm::Close(const PRUnichar* aCookie) { XMLT_LOG(mozLineTerm::Close,20, ("\n")); if (!mCookie.Equals(aCookie)) { XMLT_ERROR("mozLineTerm::Close: Error - Cookie mismatch\n"); return NS_ERROR_FAILURE; } if (mSuspended) { XMLT_ERROR("mozLineTerm::Close: Error - LineTerm %d is suspended\n", mLTerm); return NS_ERROR_FAILURE; } if (lterm_close(mLTerm) == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; } mObserver = nsnull; } /** Close LineTerm (a Finalize method) */ NS_IMETHODIMP mozLineTerm::CloseAux(void) { XMLT_LOG(mozLineTerm::CloseAux,20, ("\n")); if (lterm_close(mLTerm) == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; } mObserver = nsnull; } /** Close all LineTerm instances */ NS_IMETHODIMP mozLineTerm::CloseAllAux(void) { lterm_close_all(); return NS_OK; } /** Resizes XMLterm to match a resized window. * @param nRows number of rows * @param nCols number of columns */ NS_IMETHODIMP mozLineTerm::ResizeAux(PRInt32 nRows, PRInt32 nCols) { int retCode; XMLT_LOG(mozLineTerm::ResizeAux,30,("nRows=%d, nCols=%d\n", nRows, nCols)); // Resize LTERM retCode = lterm_resize(mLTerm, (int) nRows, (int) nCols); if (retCode < 0) return NS_ERROR_FAILURE; return NS_OK; } /** Writes a string to LTERM */ NS_IMETHODIMP mozLineTerm::Write(const PRUnichar *buf, const PRUnichar* aCookie) { if (!mCookie.Equals(aCookie)) { XMLT_ERROR("mozLineTerm::Write: Error - Cookie mismatch\n"); return NS_ERROR_FAILURE; } if (mSuspended) { XMLT_ERROR("mozLineTerm::Write: Error - LineTerm %d is suspended\n", mLTerm); return NS_ERROR_FAILURE; } XMLT_LOG(mozLineTerm::Write,30,("\n")); nsresult result; UNICHAR ubuf[MAXCOL]; int jLen, retCode; PRBool newline = PR_FALSE; jLen = 0; while ((jLen < MAXCOL-1) && (buf[jLen] != 0)) { if (buf[jLen] == U_LINEFEED) newline = PR_TRUE; ubuf[jLen] = (UNICHAR) buf[jLen]; if (ubuf[jLen] == U_PRIVATE0) { // Hack to handle input of NUL characters in NUL-terminated strings // See also: mozXMLTermKeyListener::KeyPress ubuf[jLen] = U_NUL; } jLen++; } if (jLen >= MAXCOL-1) { XMLT_ERROR("mozLineTerm::Write: Error - Buffer overflow\n"); return NS_ERROR_FAILURE; } if (mLoggingEnabled && (jLen > 0)) { /* Log all input to STDERR */ ucsprint(stderr, ubuf, jLen); nsAutoString timeStamp; result = mozXMLTermUtils::TimeStamp(60, mLastTime, timeStamp); if (NS_SUCCEEDED(result) && !timeStamp.IsEmpty()) { char* temStr = ToNewCString(timeStamp); PR_LogPrint("\n", temStr); nsMemory::Free(temStr); } else if (newline) { PR_LogPrint("\n"); } } retCode = lterm_write(mLTerm, ubuf, jLen, LTERM_WRITE_PLAIN_INPUT); if (retCode < 0) return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP mozLineTerm::Read(PRInt32 *opcodes, PRInt32 *opvals, PRInt32 *buf_row, PRInt32 *buf_col, const PRUnichar* aCookie, PRUnichar **_retval) { if (!mCookie.Equals(aCookie)) { XMLT_ERROR("mozLineTerm::Read: Error - Cookie mismatch\n"); return NS_ERROR_FAILURE; } if (mSuspended) { XMLT_ERROR("mozLineTerm::Read: Error - LineTerm %d is suspended\n", mLTerm); return NS_ERROR_FAILURE; } return ReadAux(opcodes, opvals, buf_row, buf_col, _retval, nsnull); } /** Reads a line from LTERM and returns it as a string (may be null string) */ NS_IMETHODIMP mozLineTerm::ReadAux(PRInt32 *opcodes, PRInt32 *opvals, PRInt32 *buf_row, PRInt32 *buf_col, PRUnichar **_retval, PRUnichar **retstyle) { UNICHAR ubuf[MAXCOL]; UNISTYLE ustyle[MAXCOL]; int cursor_row, cursor_col; int retCode, j; XMLT_LOG(mozLineTerm::ReadAux,30,("\n")); if (!_retval) return NS_ERROR_NULL_POINTER; retCode = lterm_read(mLTerm, 0, ubuf, MAXCOL-1, ustyle, opcodes, opvals, buf_row, buf_col, &cursor_row, &cursor_col); if (retCode < 0) return NS_ERROR_FAILURE; if (*opcodes == 0) { // Return null pointer(s) *_retval = nsnull; if (retstyle) *retstyle = nsnull; } else { // Return output string mCursorRow = cursor_row; mCursorColumn = cursor_col; XMLT_LOG(mozLineTerm::ReadAux,72,("cursor_col=%d\n", cursor_col)); int allocBytes = sizeof(PRUnichar)*(retCode + 1); *_retval = (PRUnichar*) nsMemory::Alloc(allocBytes); for (j=0; j