0001: /*
0002: ** Java cvs client library package.
0003: ** Copyright (c) 1997-2003 by Timothy Gerard Endres
0004: **
0005: ** This program is free software.
0006: **
0007: ** You may redistribute it and/or modify it under the terms of the GNU
0008: ** Library General Public License (LGPL) as published by the Free Software
0009: ** Foundation.
0010: **
0011: ** Version 2 of the license should be included with this distribution in
0012: ** the file LICENSE.txt, as well as License.html. If the license is not
0013: ** included with this distribution, you may find a copy at the FSF web
0014: ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the Free
0015: ** Software Foundation at 59 Temple Place - Suite 330, Boston, MA 02111 USA.
0016: **
0017: ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
0018: ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
0019: ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
0020: ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
0021: ** REDISTRIBUTION OF THIS SOFTWARE.
0022: **
0023: */
0024:
0025: package com.ice.cvsc;
0026:
0027: import java.io.*;
0028: import java.net.*;
0029: import java.awt.*;
0030: import java.awt.event.*;
0031: import java.util.*;
0032: import java.util.zip.*;
0033: import java.applet.*;
0034:
0035: import com.sshtools.j2ssh.SshClient;
0036: import com.sshtools.j2ssh.session.SessionChannelClient;
0037: import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
0038: import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
0039: import com.sshtools.j2ssh.configuration.SshConnectionProperties;
0040: import com.sshtools.j2ssh.transport.HostKeyVerification;
0041: import com.sshtools.j2ssh.transport.TransportProtocolException;
0042: import com.sshtools.j2ssh.transport.publickey.SshPublicKey;
0043:
0044: /**
0045: * Implements the client side of the CVS server-client protocol.
0046: * This object is used by a CVSProject object to implement the
0047: * protocols required to communicate with the CVS server and
0048: * complete CVS requests. CVSClient's use TCP communications
0049: * to a specified host and port (default is 2401). CVSClients
0050: * <em>can</em> stand on their own, however, there is not much
0051: * interesting that can be accomplished without the information
0052: * contained in a CVSProject. Typically, you use a CVSClient
0053: * by handing it a CVSRequest, and it will hand back a CVSResponse.
0054: *
0055: * Thanks to Wes Sonnenreich <wes@sonnenreich.com> for his original
0056: * attempt at the integration of MindBright's SSH package into this
0057: * client. The effort was most helpful in understanding the package.
0058: *
0059: * @version $Revision: 2.20 $
0060: * @author Timothy Gerard Endres, <a href="mailto:time@ice.com">time@ice.com</a>.
0061: * @see CVSRequest
0062: * @see CVSResponse
0063: * @see CVSProject
0064: */
0065:
0066: //
0067: // NOTES in code:
0068: //
0069: // EH-null-ui Etienne-Hugues Fortin <ehfortin@sympatico.ca>
0070: // Added a new "NullCVSUI" to ensure that the CVS UI interface is
0071: // always established.
0072: //
0073: // SW-flush-out Shawn Willden <shawn@willden.org>
0074: // Since out GZIP stream was wrapped in a BufferedOutputStream
0075: // we need to flush() to move it all out. Otherwise, the command
0076: // data sat buffered and the server never responded.
0077: //
0078:
0079: public class CVSClient extends Object implements HostKeyVerification {
0080: static public final String RCS_ID = "$Id: CVSClient.java,v 2.20 2003/07/27 04:32:56 time Exp $";
0081: static public final String RCS_REV = "$Revision: 2.20 $";
0082:
0083: public static final int DEFAULT_SSH_PORT = 22;
0084: public static final int DEFAULT_RSH_PORT = 514;
0085: public static final int DEFAULT_CVS_PORT = 2401;
0086: public static final int DEFAULT_DIR_PORT = 2402;
0087: public static final String DEFAULT_TEMP_PATH = ".";
0088:
0089: /**
0090: * Used to indicate that an ascii file is being transferred.
0091: */
0092: public static final int TRANSLATE_NONE = 0;
0093: /**
0094: * Used to indicate that a binary file is being transferred.
0095: */
0096: public static final int TRANSLATE_ASCII = 1;
0097:
0098: /**
0099: * The minimum size before a file is gzip-ed in 'gzip-file-contents' mode.
0100: */
0101: private static final int MIN_GZIP_SIZE = 1024;
0102:
0103: private static final int MAX_FILE_SIZE = 1000000;
0104: private static final boolean LIMIT_FILE_SIZE = false;
0105:
0106: private Object canLock;
0107: private boolean canceled;
0108:
0109: private String hostName;
0110: private int port;
0111: private int tempCounter;
0112:
0113: private boolean usingGZIP;
0114: private boolean serverIsOpen;
0115: private boolean tracingTCPData;
0116:
0117: private Process process;
0118: private Socket socket;
0119: private InputStream instream;
0120: private OutputStream outstream;
0121:
0122: private String tempPath;
0123: private String reason;
0124: private String recentEntryRepository;
0125:
0126: /**
0127: * Hashtable of all 'Directory' commands that we have sent
0128: * to above sending redundant commands.
0129: */
0130: private Hashtable dirHash;
0131:
0132: /**
0133: * Flag that determines whether or not we make the extra effort
0134: * to support multiple interface machines. The means we use to
0135: * determine the correct interface involves multiple socket opens,
0136: * which is expensive, so we only do it if necessary.
0137: *
0138: */
0139: private boolean supportMultipleInterfaces = false;
0140:
0141: /**
0142: * SSH supporting fields
0143: */
0144: SshClient sshClient = null;
0145: SessionChannelClient sshSession = null;
0146:
0147: /**
0148: * Creates a CVS client.
0149: * The client is unusable, however, until
0150: * the hostname and port number are established.
0151: *
0152: * @param adminDirPath The pathname of the admin ('CVS') directory.
0153: */
0154: public CVSClient() {
0155: super ();
0156:
0157: InitFields();
0158: }
0159:
0160: /**
0161: * Creates a CVS client using the provided hostname and port number.
0162: *
0163: * @param hostName The hostname of the cvs server.
0164: * @param port The port number of the cvs server (typically 2401).
0165: */
0166: public CVSClient(String hostName, int port) {
0167: super ();
0168:
0169: InitFields();
0170:
0171: this .port = port;
0172: this .hostName = hostName;
0173: }
0174:
0175: /**
0176: * Common initializer for all our contructors.
0177: */
0178: private void InitFields() {
0179: this .canceled = false;
0180: this .canLock = new Object();
0181:
0182: this .hostName = null;
0183:
0184: this .port = CVSClient.DEFAULT_CVS_PORT;
0185:
0186: this .tempPath = CVSClient.DEFAULT_TEMP_PATH;
0187:
0188: this .tempCounter = (int) (System.currentTimeMillis() % (long) 0x0FFFFFFF);
0189:
0190: this .serverIsOpen = false;
0191: this .tracingTCPData = false;
0192:
0193: this .socket = null;
0194: this .instream = null;
0195: this .outstream = null;
0196:
0197: this .reason = "";
0198: this .recentEntryRepository = "";
0199: }
0200:
0201: /**
0202: * Returns the hostname of the cvs server.
0203: */
0204: public String getHostName() {
0205: return this .hostName;
0206: }
0207:
0208: /**
0209: * Sets the hostname of the cvs server.
0210: *
0211: * @param hostName The hostname of the cvs server.
0212: */
0213: public void setHostName(String hostName) {
0214: this .hostName = hostName;
0215: }
0216:
0217: /**
0218: * Returns the port number of the cvs server.
0219: */
0220: public int getPort() {
0221: return this .port;
0222: }
0223:
0224: /**
0225: * Sets the port number of the cvs server.
0226: *
0227: * @param port The port number of the cvs server (typically 2401).
0228: */
0229: public void setPort(int port) {
0230: this .port = port;
0231: }
0232:
0233: /**
0234: * Returns the port number of the cvs server.
0235: */
0236: public boolean getMultipleInterfaceSupport() {
0237: return this .supportMultipleInterfaces;
0238: }
0239:
0240: /**
0241: * Sets the port number of the cvs server.
0242: *
0243: * @param port The port number of the cvs server (typically 2401).
0244: */
0245: public void setMultipleInterfaceSupport(boolean flag) {
0246: this .supportMultipleInterfaces = flag;
0247: CVSTracer.traceIf(flag, "Supporting multiple interfaces.");
0248: }
0249:
0250: /**
0251: * Returns the pathname of the directory in which temporary files are created.
0252: */
0253: public String getTempDirectory() {
0254: return this .tempPath;
0255: }
0256:
0257: /**
0258: * Sets the pathname of the directory in which temporary files are created.
0259: *
0260: * @param tempPath The full pathname of the temporary directory.
0261: */
0262: public void setTempDirectory(String tempPath) {
0263: this .tempPath = tempPath;
0264: }
0265:
0266: //
0267: // REVIEW
0268: // UNDONE
0269: // Should we use a StringBuffer for reason, and provide
0270: // a "appendReason()" method?
0271: //
0272:
0273: /**
0274: * Returns the reason for the last error.
0275: */
0276: public String getReason() {
0277: return this .reason;
0278: }
0279:
0280: /**
0281: * Sets the resaon for the last error.
0282: *
0283: * @param reason The string describing the reason.
0284: */
0285: public void setReason(String reason) {
0286: this .reason = reason;
0287: }
0288:
0289: /**
0290: * Indicates whether or not the connection to the server is established.
0291: */
0292: public boolean isServerOpen() {
0293: return this .serverIsOpen;
0294: }
0295:
0296: public boolean sendCVSRootDirectory(CVSRequest request) {
0297: boolean result = true;
0298:
0299: result = this .sendLine("Root " + request.getRootDirectory());
0300:
0301: return result;
0302: }
0303:
0304: /**
0305: * Send the "root" of our repository. Since all of our commands
0306: * now work with the assumption that everything is relative to
0307: * "./", we need to properly establish 'Directory .' for the
0308: * module that we are working with.
0309: *
0310: */
0311:
0312: public boolean sendRootRepository(CVSRequest request) {
0313: boolean result = true;
0314:
0315: result = this .sendLine("Directory .");
0316: result = this .sendLine(request.getRootRepository());
0317:
0318: return result;
0319: }
0320:
0321: /**
0322: * This method is used to send the 'Directory' command before
0323: * an entry is sent, to set the "context" of the entry (i.e.,
0324: * the entry's directory).
0325: *
0326: * <strong>Note</strong> that jCVS has a peculiarity. We only
0327: * send the entries the user has selected in many cases. Thus,
0328: * if we refer to a file 'com/ice/cvsc/CVSLog.java', we send
0329: * that 'Directory' command, but none for the directories 'com',
0330: * and 'ice'. In most cases, this is not an issue, but for a top
0331: * level command like Update, this causes entire branches of the
0332: * project hierarchy to be skipped because we had not sent the
0333: * 'Directory' command for that level. To solve this, whenever
0334: * we send a 'Directory' command, we send all of the intermediate
0335: * levels as well. In order to minimize the redundancy, we keep
0336: * a list of what has already been sent in 'this.dirHash'.
0337: *
0338: * @param request The current request.
0339: * @param entry The entry for which to send the command.
0340: * @return True if successful, else failure.
0341: */
0342:
0343: public boolean sendEntryRepository(CVSRequest request,
0344: CVSEntry entry) {
0345: boolean result = true;
0346:
0347: CVSTracer.traceIf(request.traceRequest, "sendEntryRepository: "
0348: + entry.dumpString());
0349:
0350: String localDir = CVSCUtilities.stripFinalSlash(entry
0351: .getLocalDirectory());
0352:
0353: CVSTracer.traceIf(request.traceRequest,
0354: "sendEntryRepository: localDir = '" + localDir + "'");
0355:
0356: if (!localDir.equals(this .recentEntryRepository)) {
0357: String stickyStr;
0358: String dirStr = localDir;
0359: String repStr = entry.getRepository();
0360:
0361: CVSTracer.traceIf(request.traceRequest,
0362: "sendEntryRepository: INITIAL \n" + " dirStr = '"
0363: + dirStr + "'\n" + " repStr = '" + repStr
0364: + "'");
0365:
0366: Vector v = new Vector();
0367: stickyStr = this .getStickTag(request, dirStr);
0368: if (stickyStr.length() > 0)
0369: v.addElement(stickyStr);
0370: v.addElement(entry.getRepository());
0371: v.addElement("Directory " + localDir);
0372: this .dirHash.put(localDir, entry.getRepository());
0373:
0374: for (int pi = 0;; ++pi) {
0375: int idxD = dirStr.lastIndexOf("/");
0376: int idxR = repStr.lastIndexOf("/");
0377:
0378: if (idxD < 0 || idxR < 0) {
0379: for (int i = v.size() - 1; i >= 0; --i) {
0380: result = this .sendLine((String) v.elementAt(i));
0381: }
0382: break;
0383: } else {
0384: dirStr = dirStr.substring(0, idxD);
0385: repStr = repStr.substring(0, idxR);
0386:
0387: CVSTracer.traceIf(request.traceRequest,
0388: "sendEntryRepository: PART [" + pi + "]\n"
0389: + " dirStr = '" + dirStr + "'\n"
0390: + " repStr = '" + repStr + "'");
0391:
0392: if (this .dirHash.get(dirStr) == null) {
0393: // NOTE These MUST be in reverse order!!!
0394: stickyStr = this .getStickTag(request, dirStr);
0395: if (stickyStr.length() > 0)
0396: v.addElement(stickyStr);
0397: v.addElement(repStr);
0398: v.addElement("Directory " + dirStr);
0399: this .dirHash.put(dirStr, repStr);
0400: }
0401: }
0402: }
0403:
0404: result = this .sendSticky(request, entry);
0405: result = this .sendStatic(request, entry);
0406:
0407: this .recentEntryRepository = localDir;
0408: }
0409:
0410: return result;
0411: }
0412:
0413: public boolean sendCVSArgument(String argument) {
0414: boolean result = true;
0415:
0416: result = this .sendLine("Argument " + argument);
0417:
0418: return result;
0419: }
0420:
0421: // REVIEW
0422: // Should be be computing it via the rootRepository and
0423: // rootDirectory?
0424: //
0425: public boolean sendCVSModule(CVSRequest request) {
0426: boolean result = true;
0427:
0428: result = this .sendCVSArgument(".");
0429:
0430: return result;
0431: }
0432:
0433: public boolean sendSetVariables(CVSRequest request) {
0434: boolean result = true;
0435:
0436: String[] vars = request.getSetVariables();
0437:
0438: if (vars != null)
0439: for (int i = 0; result && i < vars.length; ++i)
0440: result = this .sendLine("Set " + vars[i]);
0441:
0442: return result;
0443: }
0444:
0445: public boolean sendModified(CVSRequest request, CVSEntry entry,
0446: File entryFile, boolean empty, int trans) {
0447: boolean result = true;
0448:
0449: result = this .sendEntryRepository(request, entry);
0450:
0451: if (result)
0452: result = this .sendLine("Modified " + entry.getName());
0453:
0454: if (result)
0455: result = this .sendLine(entry.getModeLine());
0456:
0457: if (result) {
0458: if (empty) {
0459: result = this .sendLine("0");
0460: } else
0461: switch (trans) {
0462: case CVSClient.TRANSLATE_ASCII:
0463: result = this .sendFileAscii(entry, entryFile,
0464: request.gzipFileMode);
0465: break;
0466:
0467: default:
0468: result = this .sendFileRaw(entry, entryFile,
0469: request.gzipFileMode);
0470: break;
0471: }
0472: }
0473:
0474: if (!result) {
0475: CVSLog
0476: .logMsg("CVSClient.sendModified: ERROR sending file: "
0477: + this .getReason());
0478: }
0479:
0480: return result;
0481: }
0482:
0483: public boolean sendLostEntry(CVSRequest request, CVSEntry entry,
0484: boolean useUnchanged) {
0485: boolean result = true;
0486:
0487: CVSTracer.trace("sendLostEntry: '" + entry.getName() + "'");
0488: //
0489: // if ( request.useUnchanged == false )
0490: // If 'UseUnchanged' has NOT been sent, then lost
0491: // entries _must_ have a 'Lost' request sent.
0492: //
0493: // if ( request.useUnchanged == true )
0494: // If 'UseUnchanged' has been sent, then lost
0495: // entries are indicated by nothing being sent.
0496: //
0497: if (!useUnchanged) {
0498: result = this .sendEntryRepository(request, entry);
0499: if (result) {
0500: result = this .sendLine("Lost " + entry.getName());
0501: }
0502: }
0503:
0504: return result;
0505: }
0506:
0507: public boolean sendUnchangedEntry(CVSRequest request,
0508: CVSEntry entry, boolean useUnchanged) {
0509: boolean result = true;
0510:
0511: CVSTracer
0512: .trace("sendUnchangedEntry: '" + entry.getName() + "'");
0513:
0514: //
0515: // if ( request.useUnchanged == true )
0516: // If 'UseUnchanged' has been sent, then unchanged
0517: // entries _must_ have an 'Unchanged' request sent.
0518: //
0519: // if ( request.useUnchanged == false )
0520: // If 'UseUnchanged' has NOT been sent, then the
0521: // 'Unchanged' line is verbotten, and unchanged
0522: // entries are indicated by nothing being sent.
0523: //
0524: if (useUnchanged) {
0525: result = this .sendEntryRepository(request, entry);
0526: if (result)
0527: result = this .sendLine("Unchanged " + entry.getName());
0528: }
0529:
0530: return result;
0531: }
0532:
0533: public boolean sendCVSEntry(CVSRequest request, CVSEntry entry,
0534: File entryFile) {
0535: boolean result = true;
0536: boolean fileExists = false;
0537: boolean fileIsModified = false;
0538:
0539: CVSTracer.traceIf(request.traceRequest, "sendCVSEntry: "
0540: + entry.dumpString());
0541:
0542: // SPECIAL CASE for directories. This is currently only used when we
0543: // are adding directories, usually to support adding new files.
0544:
0545: if (entry.isDirectory()) {
0546: result = this .sendEntryRepository(request, entry);
0547:
0548: if (result) {
0549: //
0550: // SPECIAL CASE
0551: // In the case of directories, we do not send 'Entry'.
0552: // We send the 'Directory' command, which is the equivalent
0553: // of 'Entry' for directories.
0554: //
0555: result = this .sendLine("Directory "
0556: + CVSCUtilities.stripFinalSlash(entry
0557: .getFullName()));
0558:
0559: if (result) {
0560: String localDir = CVSCUtilities
0561: .stripFinalSlash(entry.getLocalDirectory());
0562:
0563: result = this .sendLine(entry.getRepository());
0564: result = this .sendSticky(request, localDir);
0565: }
0566:
0567: this .recentEntryRepository = entry.getFullName();
0568: }
0569:
0570: return result;
0571: }
0572:
0573: if (entryFile.exists()) {
0574: fileExists = true;
0575: fileIsModified = entry.isLocalFileModified(entryFile);
0576: }
0577:
0578: int trans = CVSCUtilities.computeTranslation(entry);
0579:
0580: // SPECIAL CASE when no 'Entry' lines go up...
0581:
0582: if (!request.sendEntries) {
0583: // If no 'Entry' lines, the only thing that _can_
0584: // happen is 'Modified's...
0585: if (fileIsModified || entry.isNewUserFile()
0586: || request.forceModifieds) {
0587: if (request.sendModifieds || request.forceModifieds) {
0588: result = this .sendEntryRepository(request, entry);
0589:
0590: if (result) {
0591: request.getUserInterface()
0592: .uiDisplayProgressMsg(
0593: "Uploading file '"
0594: + entry.getFullName()
0595: + "'...");
0596:
0597: result = this
0598: .sendModified(request, entry,
0599: entryFile,
0600: request.sendEmptyMods, trans);
0601: }
0602: }
0603: }
0604:
0605: return result;
0606: }
0607:
0608: // Normal case...
0609:
0610: String entryStr = entry.getServerEntryLine(entryFile.exists(),
0611: fileIsModified);
0612:
0613: result = this .sendEntryRepository(request, entry);
0614:
0615: if (result) {
0616: result = this .sendLine("Entry " + entryStr);
0617: }
0618:
0619: if (result) {
0620: if (fileExists) {
0621: if (fileIsModified || entry.isNewUserFile()
0622: || request.forceModifieds) {
0623: if (request.sendModifieds || entry.isNewUserFile()
0624: || request.forceModifieds) {
0625: request.getUserInterface()
0626: .uiDisplayProgressMsg(
0627: "Uploading file '"
0628: + entry.getName()
0629: + "'...");
0630:
0631: //
0632: // REVIEW
0633: // Here we override the 'Special Mods' flag
0634: // when there is a conflict, which appears
0635: // to be the only case where this optimization
0636: // does not work properly. However, we better
0637: // make a more thorough analysis to be certain.
0638: //
0639: boolean sendEmpties = request.sendEmptyMods;
0640: if (entry.isInConflict())
0641: sendEmpties = false;
0642:
0643: result = this .sendModified(request, entry,
0644: entryFile, sendEmpties, trans);
0645: } else {
0646: result = this .sendUnchangedEntry(request,
0647: entry, request.useUnchanged);
0648: }
0649: } else {
0650: result = this .sendUnchangedEntry(request, entry,
0651: request.useUnchanged);
0652: }
0653: } else {
0654: result = this .sendLostEntry(request, entry,
0655: request.useUnchanged);
0656: }
0657: }
0658:
0659: return result;
0660: }
0661:
0662: public boolean sendCVSEntries(CVSRequest request) {
0663: int i, count;
0664: File entryFile;
0665: CVSEntry entry;
0666: CVSEntryVector entries;
0667: boolean result = true;
0668:
0669: count = request.getEntries().size();
0670: entries = request.getEntries();
0671:
0672: for (i = 0; result && i < count; ++i) {
0673: entry = (CVSEntry) entries.elementAt(i);
0674:
0675: entryFile = request.getLocalFile(entry);
0676:
0677: result = this .sendCVSEntry(request, entry, entryFile);
0678:
0679: if (this .isCanceled())
0680: break;
0681: }
0682:
0683: return result;
0684: }
0685:
0686: public String getStickTag(CVSRequest request, String localDir) {
0687: String result = "";
0688: Hashtable stickys = request.getStickys();
0689: if (stickys != null)
0690: result = (String) stickys.get(localDir);
0691: return (result == null ? "" : result);
0692: }
0693:
0694: public boolean sendSticky(CVSRequest request, CVSEntry entry) {
0695: return this .sendSticky(request, entry.getLocalDirectory());
0696: }
0697:
0698: public boolean sendSticky(CVSRequest request, String localDir) {
0699: boolean result = true;
0700:
0701: Hashtable stickys = request.getStickys();
0702:
0703: if (stickys != null) {
0704: String tagSpec = (String) stickys.get(localDir);
0705: if (tagSpec != null && tagSpec.length() > 1) {
0706: result = this .sendLine("Sticky " + tagSpec);
0707: }
0708: }
0709:
0710: return result;
0711: }
0712:
0713: public boolean sendStatic(CVSRequest request, CVSEntry entry) {
0714: boolean result = true;
0715:
0716: Hashtable statics = request.getStatics();
0717: if (statics != null) {
0718: String isStatic = (String) statics.get(entry
0719: .getLocalDirectory());
0720:
0721: if (isStatic != null) {
0722: result = this .sendLine("Static-directory");
0723: }
0724: }
0725:
0726: return result;
0727: }
0728:
0729: public boolean sendGlobalArguments(CVSArgumentVector arguments) {
0730: int i;
0731: boolean result = true;
0732:
0733: for (i = 0; i < arguments.size(); ++i) {
0734: String argStr = arguments.argumentAt(i);
0735: result = this .sendLine("Global_option " + argStr);
0736: }
0737:
0738: return result;
0739: }
0740:
0741: public boolean sendArguments(CVSArgumentVector arguments) {
0742: int i;
0743: String argLine;
0744: boolean xArg = false;
0745: boolean result = true;
0746:
0747: for (i = 0; i < arguments.size(); ++i) {
0748: String argStr = arguments.argumentAt(i);
0749:
0750: if (argStr.indexOf('\n') < 0) {
0751: result = this .sendLine("Argument " + argStr);
0752: } else {
0753: xArg = false;
0754:
0755: StringTokenizer toker = new StringTokenizer(argStr,
0756: "\n");
0757:
0758: for (; result;) {
0759: try {
0760: argLine = toker.nextToken();
0761: } catch (NoSuchElementException ex) {
0762: break;
0763: }
0764:
0765: String prefix = (xArg ? "Argumentx " : "Argument ");
0766:
0767: result = this .sendLine(prefix + argLine);
0768: xArg = true;
0769: }
0770: }
0771: }
0772:
0773: return result;
0774: }
0775:
0776: public boolean sendEntriesArguments(CVSRequest request) {
0777: int i;
0778: CVSEntry entry;
0779: boolean result = true;
0780:
0781: CVSArgumentVector args;
0782: CVSEntryVector entries = request.getEntries();
0783:
0784: if (entries.size() < 1)
0785: return true;
0786:
0787: args = new CVSArgumentVector(entries.size());
0788:
0789: for (i = 0; i < entries.size(); ++i) {
0790: entry = (CVSEntry) entries.elementAt(i);
0791:
0792: String argName = request.execInCurDir ? entry.getName()
0793: : entry.getArgumentName();
0794:
0795: // NOTE, if we leave the trailing slash on dir
0796: // names (e.g. './subdir/'), then the server
0797: // sends use names with double slashes '//'
0798: // in the responses.
0799: //
0800: argName = CVSCUtilities.stripFinalSlash(argName);
0801:
0802: args.addElement(argName);
0803: }
0804:
0805: result = sendArguments(args);
0806:
0807: return result;
0808: }
0809:
0810: public boolean sendNotifies(CVSRequest request) {
0811: String lastWDir = "";
0812: boolean result = true;
0813: int num = request.notifies.size();
0814:
0815: for (int i = 0; result && i < num; ++i) {
0816: CVSNotifyItem notify = (CVSNotifyItem) request.notifies
0817: .elementAt(i);
0818:
0819: String dir = notify.getWorkingDirectory();
0820: if (dir.endsWith("/"))
0821: dir = dir.substring(0, dir.length() - 1);
0822:
0823: if (!lastWDir.equals(dir)) {
0824: lastWDir = dir;
0825: result = this .sendLine("Directory .");
0826: result = this .sendLine(notify.getRepository());
0827: }
0828:
0829: result = this .sendLine("Notify " + notify.getName());
0830:
0831: if (result)
0832: result = this .sendLine(notify.getServerExtra());
0833: }
0834:
0835: return result;
0836: }
0837:
0838: public CVSResponse buildErrorResponse(CVSRequest request,
0839: CVSResponse response, String message) {
0840: response.setStatus(CVSResponse.ERROR);
0841:
0842: response.appendStderr("The CVS Request failed.\n");
0843:
0844: if (message.length() > 0) {
0845: response.appendStderr(message + "\n");
0846: }
0847:
0848: if (this .getReason().length() > 0) {
0849: response.appendStderr(this .getReason() + "\n");
0850: }
0851:
0852: CVSTracer.traceIf(request.traceRequest,
0853: "CVSClient.buildErrorReponse: " + response.getStderr());
0854:
0855: return response;
0856: }
0857:
0858: public boolean performLogin(CVSRequest request) {
0859: CVSTracer.traceIf(request.traceRequest,
0860: "AUTHENTICATE: verifyOnly? '"
0861: + request.verificationOnly + "' userName '"
0862: + request.getUserName() + "' password '"
0863: + request.getPassword() + "'");
0864:
0865: this .sendLine("BEGIN "
0866: + (request.verificationOnly ? "VERIFICATION" : "AUTH")
0867: + " REQUEST");
0868:
0869: this .sendLine(request.getRootDirectory());
0870: this .sendLine(request.getUserName());
0871: this .sendLine(request.getPassword());
0872:
0873: this .sendLine("END "
0874: + (request.verificationOnly ? "VERIFICATION" : "AUTH")
0875: + " REQUEST");
0876:
0877: String reply = this .readLine();
0878:
0879: CVSTracer.traceIf(request.traceRequest,
0880: "AUTHENTICATE: REPLY: '" + reply + "'");
0881:
0882: if (reply != null)
0883: if (reply.startsWith("I LOVE YOU"))
0884: return true;
0885:
0886: if (reply != null && reply.length() > 0) {
0887: this .setReason(reply);
0888: }
0889:
0890: return false;
0891: }
0892:
0893: public boolean requestValidRequests(CVSRequest request) {
0894: boolean result = true;
0895:
0896: request.validRequests = null;
0897: request.useUnchanged = false;
0898: request.useDirectory = true;
0899:
0900: this .sendLine("valid-requests");
0901:
0902: // REVIEW
0903: // Should we clone the request and work with a copy?!
0904: //
0905: boolean saveQueue = request.queueResponse;
0906: request.queueResponse = true;
0907:
0908: CVSResponse validResponse = new CVSResponse();
0909:
0910: this .readAndParseResponse(request, validResponse);
0911:
0912: request.queueResponse = saveQueue;
0913:
0914: if (validResponse.getStatus() == CVSResponse.OK) {
0915: CVSResponseItem item = validResponse
0916: .getFirstItemByType(CVSResponseItem.VALID_REQUESTS);
0917:
0918: if (item == null) {
0919: CVSTracer
0920: .traceIf(false,
0921: "REQUEST-VALID-REQUESTS: NO VALID-REQUESTS ITEM!!");
0922: } else {
0923: String valids = item.getValidRequests();
0924: request.validRequests = valids;
0925:
0926: int index;
0927:
0928: index = valids.indexOf("Directory");
0929: if (index >= 0) {
0930: request.useDirectory = true;
0931: } else {
0932: result = false;
0933: CVSTracer
0934: .traceIf(
0935: true,
0936: "WARNING: This server does not support "
0937: + "the 'Directory' request.\n"
0938: + "jCVS will not operate properly with this server.\n"
0939: + "Please update your cvs server to release "
0940: + "1.9 or later.");
0941: }
0942:
0943: index = valids.indexOf("UseUnchanged");
0944: if (index >= 0) {
0945: request.useUnchanged = true;
0946: }
0947: }
0948: } else {
0949: request.useDirectory = true;
0950: CVSTracer
0951: .traceIf(
0952: true,
0953: "Recevied an error from the cvs server while\n"
0954: + "requesting 'valid-requests'. This is not a good sign.\n\n"
0955: + validResponse.getStdout() + "\n"
0956: + validResponse.getStderr());
0957: }
0958:
0959: return result;
0960: }
0961:
0962: /**
0963: * This method is the <em>heart</em> of the CVSClient class.
0964: * Given a CVSRequest, this method will perform all of the
0965: * processing required to open the connection, authenticate,
0966: * send all requests, read all responses, and package the
0967: * responses into a CVSResponse object, which is returned
0968: * as the result of this method. The result is guaranteed
0969: * to not be null, and will have its status set to indicate
0970: * the status of the reuest. The resulting response object
0971: * should be handed into the CVSProject's processCVSResponse()
0972: * method to process the server's reponses on the local
0973: * project contents.
0974: *
0975: * @param request The CVSRequest describing our request.
0976: */
0977: public CVSResponse processCVSRequest(CVSRequest request) {
0978: return this .processCVSRequest(request, new CVSResponse());
0979: }
0980:
0981: public boolean isCanceled() {
0982: synchronized (this .canLock) {
0983: return this .canceled;
0984: }
0985: }
0986:
0987: public void setCanceled(boolean can) {
0988: synchronized (this .canLock) {
0989: this .canceled = can;
0990: }
0991: }
0992:
0993: public boolean checkForCancel(CVSResponse response) {
0994: if (this .isCanceled()) {
0995: response.setStatus(CVSResponse.ERROR);
0996: response
0997: .appendStderr("\n*** The CVS request was canceled.\n");
0998: if (this .serverIsOpen)
0999: this .closeServer();
1000: return true;
1001: } else {
1002: return false;
1003: }
1004: }
1005:
1006: public CVSResponse processCVSRequest(CVSRequest request,
1007: CVSResponse response) {
1008: this .setCanceled(false);
1009:
1010: boolean isok = true;
1011: CVSEntryVector entries;
1012: CVSArgumentVector arguments;
1013: CVSArgumentVector globalargs;
1014:
1015: String[] vars = request.getSetVariables();
1016:
1017: this .usingGZIP = false;
1018: this .setReason("");
1019: this .recentEntryRepository = "";
1020: this .dirHash = new Hashtable();
1021:
1022: CVSUserInterface ui = request.getUserInterface();
1023:
1024: // EH-null-ui Etienne-Hugues Fortin <ehfortin@sympatico.ca>
1025: if (ui == null) {
1026: ui = this .new NullCVSUI();
1027: }
1028:
1029: this .tracingTCPData = request.traceTCPData;
1030:
1031: entries = request.getEntries();
1032: arguments = request.getArguments();
1033: globalargs = request.getGlobalArguments();
1034:
1035: if (request.traceRequest) {
1036: CVSTracer.traceIf(true, "========================"
1037: + " CVSClient.processCVSRequest "
1038: + "========================");
1039: CVSTracer.traceIf(true, " Command: "
1040: + request.getCommand());
1041: CVSTracer.traceIf(true, " Repository: "
1042: + request.getRepository());
1043: CVSTracer.traceIf(true, " RootRepository: "
1044: + request.getRootRepository());
1045: CVSTracer.traceIf(true, " CVSServer: "
1046: + request.getPort() + "@" + request.getHostName());
1047: CVSTracer.traceIf(true, " RootDirectory: "
1048: + request.getRootDirectory());
1049: CVSTracer.traceIf(true, " LocalDirectory: "
1050: + request.getLocalDirectory());
1051: CVSTracer
1052: .traceIf(
1053: true,
1054: " Connect Method: "
1055: + (request.getConnectionMethod() == CVSRequest.METHOD_RSH ? "RSH"
1056: : (request
1057: .getConnectionMethod() == CVSRequest.METHOD_SSH ? "SSH"
1058: : "INETD")));
1059: CVSTracer.traceIf(true, " Rsh Command: "
1060: + request.getRshProcess());
1061: CVSTracer.traceIf(true, " Server Command: "
1062: + request.getServerCommand());
1063: CVSTracer.traceIf(true, " isPServer? '"
1064: + (request.isPServer() ? "true " : "false") + "'"
1065: + " user '" + request.getUserName() + "'"
1066: + " pass '" + request.getPassword() + "'");
1067: CVSTracer.traceIf(true, " There are "
1068: + (vars == null ? "no" : ("" + vars.length))
1069: + " user set variables.");
1070: CVSTracer.traceIf(true, " NumEntries: "
1071: + (entries == null ? 0 : entries.size())
1072: + " NumArguments: "
1073: + (arguments == null ? 0 : arguments.size()));
1074: CVSTracer.traceIf(true, " GlobalOptions: "
1075: + (globalargs == null ? 0 : globalargs.size())
1076: + " GzipStreamLevel: "
1077: + request.getGzipStreamLevel());
1078: CVSTracer.traceIf(true, " redirectOutput '"
1079: + (request.redirectOutput ? "true " : "false")
1080: + "'" + " execInCurDir '"
1081: + (request.execInCurDir ? "true " : "false") + "'");
1082: CVSTracer.traceIf(true, " sendEntries '"
1083: + (request.sendEntries ? "true " : "false") + "'"
1084: + " sendEntryfiles '"
1085: + (request.sendEntryFiles ? "true " : "false")
1086: + "'");
1087: CVSTracer
1088: .traceIf(true, " sendModifieds '"
1089: + (request.sendModifieds ? "true "
1090: : "false")
1091: + "'"
1092: + " sendEmptyMods '"
1093: + (request.sendEmptyMods ? "true "
1094: : "false") + "'");
1095: CVSTracer.traceIf(true, " sendArguments '"
1096: + (request.sendArguments ? "true " : "false") + "'"
1097: + " ignoreResult '"
1098: + (request.ignoreResult ? "true " : "false") + "'");
1099: CVSTracer.traceIf(true, " sendModule '"
1100: + (request.sendModule ? "true " : "false") + "'"
1101: + " allowOverWrites '"
1102: + (request.allowOverWrites ? "true " : "false")
1103: + "'");
1104: CVSTracer
1105: .traceIf(true, " displayReponse '"
1106: + (request.displayReponse ? "true "
1107: : "false")
1108: + "'"
1109: + " handleUpdated '"
1110: + (request.handleUpdated ? "true "
1111: : "false") + "'");
1112: CVSTracer.traceIf(true, " handleMerged '"
1113: + (request.handleMerged ? "true " : "false") + "'"
1114: + " handleCopyFile '"
1115: + (request.handleCopyFile ? "true " : "false")
1116: + "'");
1117: CVSTracer.traceIf(true, " handleEntries '"
1118: + (request.handleEntries ? "true " : "false") + "'"
1119: + " handleFlags '"
1120: + (request.handleFlags ? "true " : "false") + "'");
1121: CVSTracer.traceIf(true, " queueResponse '"
1122: + (request.queueResponse ? "true " : "false")
1123: + "'"
1124: + " responseHandler '"
1125: + (request.responseHandler == null ? "null "
1126: : request.responseHandler.getClass()
1127: .getName()) + "'");
1128: CVSTracer.traceIf(true, " includeNotifies '"
1129: + (request.includeNotifies ? "true " : "false")
1130: + "'"
1131: + " notifiesSize '"
1132: + (request.notifies == null ? "null"
1133: : ("" + request.notifies.size())) + "'");
1134:
1135: CVSTracer.traceIf(request.traceRequest,
1136: "***************************************"
1137: + "**************************************");
1138: } // if ( request.traceRequest )
1139:
1140: // SPECIAL HACKS
1141: //
1142: // For the "ci" command (commit), if the user has provided
1143: // the '-f' option, then we need to force all files to go
1144: // up as 'Modified'.
1145: //
1146: if ("ci".equals(request.getCommand())) {
1147: if (request.getArguments().containsArgument("-f")) {
1148: CVSTracer.traceIf(request.traceRequest,
1149: "SPECIAL CASE: Forcing all files to be "
1150: + "'Modified' for '-f' commit.");
1151: request.forceModifieds = true;
1152: }
1153: }
1154:
1155: int portNum = request.getPort();
1156:
1157: if (portNum == 0) {
1158: CVSTracer.traceIf(request.traceRequest,
1159: "CVSRequest: default port number to '" + this .port
1160: + "'");
1161: portNum = this .port;
1162: }
1163:
1164: if (this .checkForCancel(response)) {
1165: return response;
1166: }
1167:
1168: CVSTracer.traceIf(request.traceRequest,
1169: "CVSRequest: opening server...");
1170:
1171: ui.uiDisplayProgressMsg("Opening server '" + request.getPort()
1172: + "@" + request.getHostName() + "'...");
1173:
1174: isok = this .openServer(request);
1175:
1176: CVSTracer.traceIf(request.traceRequest,
1177: "CVSRequest: server is " + (isok ? "" : "not ")
1178: + "open.");
1179:
1180: if (!isok) {
1181: String why = this .getReason();
1182:
1183: this .buildErrorResponse(request, response,
1184: "Failed to open socket to connect to cvs server '"
1185: + request.getPort() + "@"
1186: + request.getHostName() + "'.\n" + why);
1187:
1188: ui.uiDisplayProgressMsg("Failed to open '"
1189: + request.getPort() + "@" + request.getHostName()
1190: + "'.");
1191:
1192: return response;
1193: }
1194:
1195: if (this .checkForCancel(response)) {
1196: return response;
1197: }
1198:
1199: if (request.isPServer()) {
1200: if (request.getUserName() == null
1201: || request.getPassword() == null) {
1202: this
1203: .buildErrorResponse(
1204: request,
1205: response,
1206: "Attempted to connect to a password"
1207: + " cvs server without a "
1208: + ((request.getUserName() == null) ? "username"
1209: : "password") + ".\n");
1210:
1211: ui
1212: .uiDisplayProgressMsg("Incomplete login. Request canceled.");
1213:
1214: return response;
1215: }
1216:
1217: ui.uiDisplayProgressMsg("Authenticating '"
1218: + request.getUserName() + "@"
1219: + request.getHostName() + "'...");
1220:
1221: if (!this .performLogin(request)) {
1222: this .buildErrorResponse(request, response,
1223: "Failed authentication with the user name '"
1224: + request.getUserName() + "'.\n");
1225:
1226: ui.uiDisplayProgressMsg("Authentication of '"
1227: + request.getUserName() + "@"
1228: + request.getHostName() + "' failed.");
1229:
1230: return response;
1231: }
1232: }
1233:
1234: if (request.verificationOnly) {
1235: String authResultStr = "Authentication of '"
1236: + request.getUserName() + "@"
1237: + request.getHostName() + "' succeeded.";
1238:
1239: ui.uiDisplayProgressMsg(authResultStr);
1240:
1241: response.setStatus(CVSResponse.OK);
1242: response.appendStderr(authResultStr);
1243:
1244: this .closeServer();
1245:
1246: return response;
1247: }
1248:
1249: if (this .checkForCancel(response)) {
1250: return response;
1251: }
1252:
1253: if (isok) {
1254: isok = this .requestValidRequests(request);
1255: }
1256:
1257: CVSTracer.traceIf(request.traceRequest,
1258: "Valid Requests: useUnchanged '"
1259: + (request.useUnchanged ? "true" : "false")
1260: + "'" + " useDirectory '"
1261: + (request.useDirectory ? "true" : "false")
1262: + "'");
1263:
1264: if (this .checkForCancel(response)) {
1265: return response;
1266: }
1267:
1268: if (isok && request.sendRootDirectory) {
1269: CVSTracer.traceIf(request.traceRequest,
1270: "CVSRequest: send root directory...");
1271:
1272: // SPECIAL-CASE (?)
1273: // If there's no rootDirectory, then don't
1274: // send the command. This is only used by the
1275: // 'noop' used by the Test Connection dialog.
1276: //
1277: if (request.getRootDirectory().length() > 0) {
1278: isok = this .sendCVSRootDirectory(request);
1279: }
1280: }
1281:
1282: // Establish GzipStream is requested (level > 0).
1283: //
1284: if (isok && request.gzipStreamLevel > 0
1285: && request.validRequests != null
1286: && request.validRequests.indexOf("Gzip-stream") >= 0) {
1287: CVSTracer.traceIf(request.traceRequest,
1288: "Utilitizing Gzip-stream mode at level 6.");
1289: this .usingGZIP = true;
1290: this .sendLine("Gzip-stream 6");
1291: this .instream = new InflaterInputStream(this .instream);
1292: this .outstream = new DeflaterOutputStream(this .outstream);
1293: }
1294:
1295: if (isok) {
1296: isok = this .sendSetVariables(request);
1297: }
1298:
1299: ui.uiDisplayProgressMsg("Negotiating cvs protocol...");
1300:
1301: this .sendValidResponses(request, "");
1302:
1303: ui.uiDisplayProgressMsg("Sending command request, '"
1304: + request.getCommand() + "'...");
1305:
1306: if (isok
1307: && request.allowGzipFileMode
1308: && (!this .usingGZIP)
1309: && request.validRequests != null
1310: && request.validRequests.indexOf("gzip-file-contents") >= 0) {
1311: CVSTracer.traceIf(request.traceRequest,
1312: "Utilitizing gzip-file-contents mode at level 6.");
1313:
1314: this .sendLine("gzip-file-contents 6");
1315: request.gzipFileMode = true;
1316: }
1317:
1318: if (this .checkForCancel(response)) {
1319: return response;
1320: }
1321:
1322: CVSArgumentVector globalArgs = request.getGlobalArguments();
1323: if (isok & globalArgs != null && globalArgs.size() > 0) {
1324: isok = this .sendGlobalArguments(globalArgs);
1325: }
1326:
1327: if (isok & request.notifies != null
1328: && request.notifies.size() > 0) {
1329: isok = this .sendNotifies(request);
1330: }
1331:
1332: // NOTE The "request.sendEntries" flag is not checked here!
1333: // It is utilized inside sendCVSEntries().
1334: if (isok) {
1335: CVSTracer.traceIf(request.traceRequest,
1336: "CVSRequest: send entries...");
1337: isok = this .sendCVSEntries(request);
1338: }
1339:
1340: if (this .checkForCancel(response)) {
1341: return response;
1342: }
1343:
1344: if (isok && request.sendRootDirectory) {
1345: if (request.execInCurDir && request.getDirEntry() != null) {
1346: // Set the 'current directory'...
1347: CVSTracer.traceIf(request.traceRequest,
1348: "CVSRequest: send 'current' directory...");
1349: this .recentEntryRepository = ""; // make sure it goes...
1350: isok = this .sendEntryRepository(request, request
1351: .getDirEntry());
1352: } else {
1353: // Reset the 'current directory' to the top level...
1354: CVSTracer.traceIf(request.traceRequest,
1355: "CVSRequest: send root repository...");
1356:
1357: isok = this .sendRootRepository(request);
1358: }
1359: }
1360:
1361: if (this .checkForCancel(response)) {
1362: return response;
1363: }
1364:
1365: if (isok && request.sendArguments) {
1366: CVSTracer.traceIf(request.traceRequest,
1367: "CVSRequest: send arguments...");
1368: isok = this .sendArguments(request.getArguments());
1369: }
1370:
1371: if (this .checkForCancel(response)) {
1372: return response;
1373: }
1374:
1375: if (isok && request.sendEntryFiles) {
1376: CVSTracer.traceIf(request.traceRequest,
1377: "CVSRequest: send files...");
1378: isok = this .sendEntriesArguments(request);
1379: }
1380:
1381: if (this .checkForCancel(response)) {
1382: return response;
1383: }
1384:
1385: if (isok && request.sendModule) {
1386: CVSTracer.traceIf(request.traceRequest,
1387: "CVSRequest: send module name...");
1388: isok = this .sendCVSModule(request);
1389: }
1390:
1391: if (isok) {
1392: CVSTracer.traceIf(request.traceRequest,
1393: "CVSRequest: send command '" + request.getCommand()
1394: + "'");
1395:
1396: isok = this .sendLine(request.getCommand());
1397: }
1398:
1399: if (this .checkForCancel(response)) {
1400: return response;
1401: }
1402:
1403: try {
1404: if (usingGZIP) {
1405: ((DeflaterOutputStream) this .outstream).finish();
1406:
1407: // SW-flush-output
1408: // Since out GZIP stream was wrapped in a BufferedOutputStream
1409: // we need to flush() to move it all out.
1410: //
1411: outstream.flush();
1412: }
1413: } catch (IOException ex) {
1414: ex.printStackTrace();
1415: }
1416:
1417: if (isok) {
1418: CVSTracer.traceIf(request.traceRequest,
1419: "CVSRequest: reading response...");
1420:
1421: ui.uiDisplayProgressMsg("Reading server response...");
1422:
1423: this .readAndParseResponse(request, response);
1424: } else {
1425: CVSTracer.traceIf(request.traceRequest,
1426: "CVSRequest: Error sending command request.");
1427:
1428: ui.uiDisplayProgressMsg("Error sending command request...");
1429:
1430: this
1431: .buildErrorResponse(
1432: request,
1433: response,
1434: "during processing of cvs request"
1435: + ((this .getReason().length() < 1) ? ""
1436: : (": {" + this .getReason() + "}")));
1437: }
1438:
1439: ui.uiDisplayProgressMsg("Closing CVS server connection.");
1440:
1441: this .closeServer();
1442:
1443: if (this .checkForCancel(response)) {
1444: return response;
1445: }
1446:
1447: CVSTracer.traceIf(request.traceRequest,
1448: "**====================================="
1449: + "====================================**");
1450:
1451: ui.uiDisplayProgressMsg("Command completed with '"
1452: + (response.getStatus() == CVSResponse.OK ? "ok"
1453: : "error") + "' status.");
1454:
1455: return response;
1456: }
1457:
1458: private String generateTempName() {
1459: this .tempCounter++;
1460:
1461: String randStr = Long
1462: .toHexString(this .tempCounter % 0x0FFFFFFF);
1463:
1464: if (randStr.length() > 7) {
1465: randStr = randStr.substring(randStr.length() - 7);
1466: }
1467:
1468: String result = "T" + randStr + ".cvs";
1469:
1470: CVSTracer.traceIf(false, "TEMPFILE: counter '"
1471: + this .tempCounter + "' name '" + result + "'");
1472:
1473: return result;
1474: }
1475:
1476: public String generateTempPath() {
1477: String path = null;
1478:
1479: for (;;) {
1480: path = this .tempPath + "/" + this .generateTempName();
1481:
1482: File tFile = new File(path);
1483:
1484: if (!tFile.exists())
1485: break;
1486:
1487: if (true)
1488: CVSTracer
1489: .traceWithStack("CVSClient.generateTempPath: ERROR '"
1490: + path + "' exists!");
1491: }
1492:
1493: return path;
1494: }
1495:
1496: private boolean requestIsQueued(CVSRequest request) {
1497: return (request.queueResponse || request.responseHandler == null);
1498: }
1499:
1500: private boolean processResponseItem(CVSRequest request,
1501: CVSResponse response, CVSResponseItem item) {
1502: boolean result = true;
1503:
1504: // NOTE
1505: // SPECIAL CASE
1506: // We need to handle the local directories returned when we use
1507: // the "exec in directory" feature. This is because the paths
1508: // being returned are relative to this directory, and correcting
1509: // the path here is the simplest and best way to fix it.
1510: //
1511: if (request.execInCurDir && request.getDirEntry() != null) {
1512: String itemPath = item.getPathName();
1513: String pfxPath = request.getDirEntry().getLocalPathName();
1514:
1515: if (itemPath != null) {
1516: if (itemPath.startsWith("./"))
1517: itemPath = pfxPath + itemPath.substring(2);
1518: else
1519: itemPath = pfxPath + itemPath;
1520:
1521: item.setPathName(itemPath);
1522: }
1523: }
1524:
1525: if (this .requestIsQueued(request)) {
1526: response.addResponseItem(item);
1527: } else {
1528: result = request.responseHandler.handleResponseItem(
1529: request, response, item);
1530: }
1531:
1532: return result;
1533: }
1534:
1535: public CVSResponse readAndParseResponse(CVSRequest request,
1536: CVSResponse response) {
1537: boolean isok;
1538: int status = CVSResponse.OK;
1539: boolean gotStatus = false;
1540: int fileSize;
1541: int index;
1542: String line = null;
1543:
1544: CVSResponseItem currItem = null;
1545:
1546: for (isok = true; isok;) {
1547: if (this .isCanceled())
1548: break;
1549:
1550: line = this .readLine();
1551:
1552: if (line == null) {
1553: CVSTracer.traceIf(request.traceResponse,
1554: "PARSE: End of file on input stream.");
1555: break;
1556: }
1557:
1558: CVSTracer.traceIf(false,
1559: "CVSClient.readAndParseResponse: INLINE '"
1560: + line
1561: + "' currItem '"
1562: + (currItem == null ? "(null)" : currItem
1563: .toString()) + "'");
1564:
1565: if (currItem != null) {
1566: int itemType = currItem.getType();
1567:
1568: if (currItem.getAddState() == CVSResponseItem.GET_FULL_PATH) {
1569: CVSTracer.traceIf(request.traceResponse,
1570: "PARSE: FullPath '" + line + "'");
1571:
1572: if (line.endsWith("/./")) {
1573: // SPECIAL CASE
1574: // When the user does something like "-d ." on
1575: // an update or checkout, we will get repository
1576: // names that look like "/usr/cvsroot/path/./".
1577: // This confuses our code, so we adjust here...
1578: line = line.substring(0, line.length() - 2);
1579: CVSTracer.traceIf(request.traceResponse,
1580: "PARSE: Adjusted FullPath '" + line
1581: + "'");
1582: }
1583:
1584: currItem.setRepositoryName(line);
1585: } else if (currItem.getAddState() == CVSResponseItem.GET_ENTRIES_LINE) {
1586: CVSTracer.traceIf(request.traceResponse,
1587: "PARSE: Entry '" + line + "'");
1588: currItem.setEntriesLine(line);
1589: } else if (currItem.getAddState() == CVSResponseItem.GET_MODE_LINE) {
1590: CVSTracer.traceIf(request.traceResponse,
1591: "PARSE: Mode '" + line + "'");
1592: currItem.setModeLine(line);
1593: } else if (currItem.getAddState() == CVSResponseItem.GET_TAG_SPEC) {
1594: CVSTracer.traceIf(request.traceResponse,
1595: "PARSE: Tag Spec '" + line + "'");
1596: currItem.setTagSpec(line);
1597: } else if (currItem.getAddState() == CVSResponseItem.GET_PROGRAM) {
1598: CVSTracer.traceIf(request.traceResponse,
1599: "PARSE: Program Name '" + line + "'");
1600: currItem.setProgram(line);
1601: } else if (currItem.getAddState() == CVSResponseItem.GET_NEW_NAME) {
1602: CVSTracer.traceIf(request.traceResponse,
1603: "PARSE: New Name '" + line + "'");
1604: currItem.setNewName(line);
1605: }
1606:
1607: switch (itemType) {
1608: case CVSResponseItem.CREATED:
1609: case CVSResponseItem.MERGED:
1610: case CVSResponseItem.PATCHED:
1611: case CVSResponseItem.UPDATED:
1612: case CVSResponseItem.UPDATE_EXISTING:
1613: String itemCmdName = (itemType == CVSResponseItem.CREATED ? "Created"
1614: : (itemType == CVSResponseItem.MERGED ? "Merged"
1615: : (itemType == CVSResponseItem.PATCHED ? "Patched"
1616: : (itemType == CVSResponseItem.UPDATED ? "Updated"
1617: : "Update-existing"))));
1618:
1619: switch (currItem.getAddState()) {
1620: case CVSResponseItem.GET_FULL_PATH:
1621: currItem
1622: .setAddState(CVSResponseItem.GET_ENTRIES_LINE);
1623: break;
1624:
1625: case CVSResponseItem.GET_ENTRIES_LINE:
1626: currItem
1627: .setAddState(CVSResponseItem.GET_MODE_LINE);
1628: break;
1629:
1630: case CVSResponseItem.GET_MODE_LINE:
1631: currItem.setAddState(CVSResponseItem.GET_FILE);
1632:
1633: File file = new File(this .generateTempPath());
1634:
1635: String name = currItem.getRepositoryName();
1636: index = name.lastIndexOf('/');
1637: if (index >= 0)
1638: name = name.substring(index + 1);
1639: name = currItem.getPathName() + name;
1640:
1641: // Only display this when queue-ing, since
1642: // the processing typically follows immediately
1643: // with its own message...
1644: if (this .requestIsQueued(request))
1645: request.getUserInterface()
1646: .uiDisplayProgressMsg(
1647: "Downloading file '" + name
1648: + "'...");
1649:
1650: if (this .retrieveFile(currItem, file)) {
1651: currItem.setFile(file);
1652: isok = this .processResponseItem(request,
1653: response, currItem);
1654: } else {
1655: response.appendStdErr("ERROR downloading '"
1656: + itemCmdName + "' file '" + name
1657: + "'\n into temporary file '"
1658: + file.getPath() + "'.\n");
1659: response.appendStdErr("REASON "
1660: + this .getReason() + "\n");
1661:
1662: status = CVSResponse.ERROR;
1663: }
1664:
1665: currItem = null;
1666: break;
1667: }
1668: break;
1669:
1670: case CVSResponseItem.CHECKED_IN:
1671: case CVSResponseItem.NEW_ENTRY:
1672: if (currItem.getAddState() == CVSResponseItem.GET_FULL_PATH) {
1673: currItem
1674: .setAddState(CVSResponseItem.GET_ENTRIES_LINE);
1675: } else {
1676: isok = this .processResponseItem(request,
1677: response, currItem);
1678: currItem = null;
1679: }
1680: break;
1681:
1682: case CVSResponseItem.COPY_FILE:
1683: if (currItem.getAddState() == CVSResponseItem.GET_FULL_PATH) {
1684: currItem
1685: .setAddState(CVSResponseItem.GET_NEW_NAME);
1686: } else {
1687: isok = this .processResponseItem(request,
1688: response, currItem);
1689: currItem = null;
1690: }
1691: break;
1692:
1693: case CVSResponseItem.SET_STICKY:
1694: if (currItem.getAddState() == CVSResponseItem.GET_FULL_PATH) {
1695: currItem
1696: .setAddState(CVSResponseItem.GET_TAG_SPEC);
1697: } else {
1698: isok = this .processResponseItem(request,
1699: response, currItem);
1700: currItem = null;
1701: }
1702: break;
1703:
1704: case CVSResponseItem.NOTIFIED:
1705: case CVSResponseItem.REMOVED:
1706: case CVSResponseItem.REMOVE_ENTRY:
1707:
1708: case CVSResponseItem.SET_CHECKIN_PROG:
1709: case CVSResponseItem.SET_UPDATE_PROG:
1710:
1711: case CVSResponseItem.CLEAR_STICKY:
1712: case CVSResponseItem.SET_STATIC_DIR:
1713: case CVSResponseItem.CLEAR_STATIC_DIR:
1714:
1715: isok = this .processResponseItem(request, response,
1716: currItem);
1717: currItem = null;
1718:
1719: break;
1720:
1721: default:
1722: CVSLog
1723: .logMsg("PARSE: ERROR unknown currentItem type '"
1724: + currItem.getType() + "'");
1725: break;
1726: }
1727: } else if (line.startsWith("ok")) {
1728: CVSTracer.traceIf(request.traceResponse, "PARSE: ok");
1729: response.setStatus(CVSResponse.OK);
1730: gotStatus = true;
1731: break;
1732: } else if (line.startsWith("error")) {
1733: CVSTracer.traceIf(request.traceResponse,
1734: "PARSE: error '" + line + "'");
1735:
1736: gotStatus = true;
1737:
1738: String errCodeStr = "";
1739: String errTextStr = "";
1740:
1741: if (line.length() > 6) {
1742: line = line.substring(6);
1743:
1744: if (line.startsWith(" ")) {
1745: errCodeStr = "";
1746: errTextStr = line.substring(1);
1747: } else {
1748: index = line.indexOf(' ');
1749: if (index > 0) {
1750: errCodeStr = line.substring(0, index);
1751: errTextStr = line.substring(index + 1);
1752: } else {
1753: errCodeStr = "";
1754: errTextStr = line;
1755: }
1756: }
1757: }
1758:
1759: response.setErrorStatus(errCodeStr, errTextStr);
1760:
1761: break;
1762: } else if (line.startsWith("I LOVE YOU")) {
1763: // NOTE
1764: // We pick up these here, since there might be a
1765: // case where we failed to recognize we're a pserver?
1766: CVSLog.logMsg("PARSE: GOT LOVE MESSAGE '" + line + "'");
1767: // continue on, since we are loved
1768: } else if (line.startsWith("I HATE YOU")) {
1769: // NOTE
1770: // We pick up these here, since there might be a
1771: // case where we failed to recognize we're a pserver?
1772: CVSLog.logMsg("PARSE: GOT HATE MESSAGE '" + line + "'");
1773:
1774: gotStatus = true;
1775: response.setErrorStatus("-1", "INVALID LOGIN");
1776:
1777: // We SHOULD break, since there may never be more data...
1778: break;
1779: } else if (line.startsWith("Updated ")) {
1780: String pathName = line.substring(8);
1781: CVSTracer.traceIf(request.traceResponse,
1782: "PARSE: Update '" + pathName + "'");
1783:
1784: CVSResponseItem newItem = new CVSResponseItem(
1785: CVSResponseItem.UPDATED);
1786:
1787: newItem.setPathName(pathName);
1788:
1789: newItem
1790: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1791: : CVSResponseItem.GET_ENTRIES_LINE);
1792:
1793: // UNDONE - its an ERROR IF currentItem is NOT NULL!!
1794: currItem = newItem;
1795: } else if (line.startsWith("Merged ")) {
1796: String pathName = line.substring(7);
1797: CVSTracer.traceIf(request.traceResponse,
1798: "PARSE: Merged '" + pathName + "'");
1799:
1800: CVSResponseItem newItem = new CVSResponseItem(
1801: CVSResponseItem.MERGED);
1802:
1803: newItem.setPathName(pathName);
1804:
1805: newItem
1806: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1807: : CVSResponseItem.GET_ENTRIES_LINE);
1808:
1809: currItem = newItem;
1810: } else if (line.startsWith("Update-existing ")) {
1811: String pathName = line.substring(16);
1812: CVSTracer.traceIf(request.traceResponse,
1813: "PARSE: Update-existing '" + pathName + "'");
1814:
1815: CVSResponseItem newItem = new CVSResponseItem(
1816: CVSResponseItem.UPDATE_EXISTING);
1817:
1818: newItem.setPathName(pathName);
1819:
1820: newItem
1821: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1822: : CVSResponseItem.GET_ENTRIES_LINE);
1823:
1824: currItem = newItem;
1825: } else if (line.startsWith("Created ")) {
1826: String pathName = line.substring(8);
1827: CVSTracer.traceIf(request.traceResponse,
1828: "PARSE: Created '" + pathName + "'");
1829:
1830: CVSResponseItem newItem = new CVSResponseItem(
1831: CVSResponseItem.CREATED);
1832:
1833: newItem.setPathName(pathName);
1834:
1835: newItem
1836: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1837: : CVSResponseItem.GET_ENTRIES_LINE);
1838:
1839: currItem = newItem;
1840: } else if (line.startsWith("Patched ")) {
1841: String pathName = line.substring(8);
1842: CVSTracer.traceIf(request.traceResponse,
1843: "PARSE: Patched '" + pathName + "'");
1844:
1845: CVSResponseItem newItem = new CVSResponseItem(
1846: CVSResponseItem.PATCHED);
1847:
1848: newItem.setPathName(pathName);
1849:
1850: newItem
1851: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1852: : CVSResponseItem.GET_ENTRIES_LINE);
1853:
1854: currItem = newItem;
1855: } else if (line.startsWith("Checksum ")) {
1856: String sumStr = line.substring(9);
1857: CVSTracer.traceIf(request.traceResponse,
1858: "PARSE: Checksum '" + sumStr + "'");
1859:
1860: CVSResponseItem newItem = new CVSResponseItem(
1861: CVSResponseItem.CHECKSUM);
1862:
1863: newItem.setChecksum(sumStr);
1864:
1865: isok = this .processResponseItem(request, response,
1866: newItem);
1867: } else if (line.startsWith("Module-expansion ")) {
1868: String pathName = line.substring(17);
1869: CVSTracer.traceIf(request.traceResponse,
1870: "PARSE: Module-expansion '" + pathName + "'");
1871:
1872: CVSResponseItem newItem = new CVSResponseItem(
1873: CVSResponseItem.MODULE_EXPANSION);
1874:
1875: newItem.setPathName(pathName);
1876: //
1877: // UNDONE
1878: // REVIEW
1879: // If module-expansions send two line pathnames when
1880: // 'Directory' is in use, we will be out of sync!!!
1881: //
1882: isok = this .processResponseItem(request, response,
1883: newItem);
1884: } else if (line.startsWith("Notified ")) {
1885: String pathName = line.substring(9);
1886: CVSTracer.traceIf(request.traceResponse,
1887: "PARSE: Notified '" + pathName + "'");
1888:
1889: CVSResponseItem newItem = new CVSResponseItem(
1890: CVSResponseItem.NOTIFIED);
1891:
1892: newItem.setPathName(pathName);
1893:
1894: if (request.useDirectory) {
1895: currItem = newItem;
1896: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
1897: } else {
1898: isok = this .processResponseItem(request, response,
1899: newItem);
1900: }
1901: } else if (line.startsWith("Removed ")) {
1902: String pathName = line.substring(8);
1903: CVSTracer.traceIf(request.traceResponse,
1904: "PARSE: Removed '" + pathName + "'");
1905:
1906: CVSResponseItem newItem = new CVSResponseItem(
1907: CVSResponseItem.REMOVED);
1908:
1909: newItem.setPathName(pathName);
1910:
1911: if (request.useDirectory) {
1912: currItem = newItem;
1913: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
1914: } else {
1915: isok = this .processResponseItem(request, response,
1916: newItem);
1917: }
1918: } else if (line.startsWith("Remove-entry ")) {
1919: String pathName = line.substring(13);
1920: CVSTracer.traceIf(request.traceResponse,
1921: "PARSE: Remove-entry '" + pathName + "'");
1922:
1923: CVSResponseItem newItem = new CVSResponseItem(
1924: CVSResponseItem.REMOVE_ENTRY);
1925:
1926: newItem.setPathName(pathName);
1927:
1928: if (request.useDirectory) {
1929: currItem = newItem;
1930: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
1931: } else {
1932: isok = this .processResponseItem(request, response,
1933: newItem);
1934: }
1935: } else if (line.startsWith("Checked-in ")) {
1936: String pathName = line.substring(11);
1937: CVSTracer.traceIf(request.traceResponse,
1938: "PARSE: Checked-in '" + pathName + "'");
1939:
1940: CVSResponseItem newItem = new CVSResponseItem(
1941: CVSResponseItem.CHECKED_IN);
1942:
1943: newItem.setPathName(pathName);
1944:
1945: newItem
1946: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1947: : CVSResponseItem.GET_ENTRIES_LINE);
1948:
1949: currItem = newItem;
1950: } else if (line.startsWith("New-entry ")) {
1951: String pathName = line.substring(10);
1952: CVSTracer.traceIf(request.traceResponse,
1953: "PARSE: New-entry '" + pathName + "'");
1954:
1955: CVSResponseItem newItem = new CVSResponseItem(
1956: CVSResponseItem.NEW_ENTRY);
1957:
1958: newItem.setPathName(pathName);
1959:
1960: newItem
1961: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1962: : CVSResponseItem.GET_ENTRIES_LINE);
1963:
1964: currItem = newItem;
1965: } else if (line.startsWith("Copy-file ")) {
1966: String pathName = line.substring(10);
1967: CVSTracer.traceIf(request.traceResponse,
1968: "PARSE: Copy-file '" + pathName + "'");
1969:
1970: CVSResponseItem newItem = new CVSResponseItem(
1971: CVSResponseItem.COPY_FILE);
1972:
1973: newItem.setPathName(pathName);
1974:
1975: newItem
1976: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1977: : CVSResponseItem.GET_NEW_NAME);
1978:
1979: currItem = newItem;
1980: } else if (line.startsWith("Set-sticky ")) {
1981: String pathName = line.substring(11);
1982: CVSTracer.traceIf(request.traceResponse,
1983: "PARSE: Set-sticky '" + pathName + "'");
1984:
1985: CVSResponseItem newItem = new CVSResponseItem(
1986: CVSResponseItem.SET_STICKY);
1987:
1988: newItem.setPathName(pathName);
1989:
1990: newItem
1991: .setAddState(request.useDirectory ? CVSResponseItem.GET_FULL_PATH
1992: : CVSResponseItem.GET_TAG_SPEC);
1993:
1994: currItem = newItem;
1995: } else if (line.startsWith("Clear-sticky ")) {
1996: String pathName = line.substring(13);
1997: CVSTracer.traceIf(request.traceResponse,
1998: "PARSE: Clear-sticky '" + pathName + "'");
1999:
2000: CVSResponseItem newItem = new CVSResponseItem(
2001: CVSResponseItem.CLEAR_STICKY);
2002:
2003: newItem.setPathName(pathName);
2004:
2005: if (request.useDirectory) {
2006: currItem = newItem;
2007: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
2008: } else {
2009: isok = this .processResponseItem(request, response,
2010: newItem);
2011: }
2012: } else if (line.startsWith("Set-static-directory ")) {
2013: String pathName = line.substring(21);
2014: CVSTracer.traceIf(request.traceResponse,
2015: "PARSE: Set-static-directory '" + pathName
2016: + "'");
2017:
2018: CVSResponseItem newItem = new CVSResponseItem(
2019: CVSResponseItem.SET_STATIC_DIR);
2020:
2021: newItem.setPathName(pathName);
2022:
2023: if (request.useDirectory) {
2024: currItem = newItem;
2025: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
2026: } else {
2027: isok = this .processResponseItem(request, response,
2028: newItem);
2029: }
2030: } else if (line.startsWith("Clear-static-directory ")) {
2031: String pathName = line.substring(23);
2032: CVSTracer.traceIf(request.traceResponse,
2033: "PARSE: Clear-static-directory '" + pathName
2034: + "'");
2035:
2036: CVSResponseItem newItem = new CVSResponseItem(
2037: CVSResponseItem.CLEAR_STATIC_DIR);
2038:
2039: newItem.setPathName(pathName);
2040:
2041: if (request.useDirectory) {
2042: currItem = newItem;
2043: newItem.setAddState(CVSResponseItem.GET_FULL_PATH);
2044: } else {
2045: isok = this .processResponseItem(request, response,
2046: newItem);
2047: }
2048: } else if (line.startsWith("Set-checkin-prog ")) {
2049: String pathName = line.substring(17);
2050: CVSTracer.traceIf(request.traceResponse,
2051: "PARSE: Set-checkin-prog '" + pathName + "'");
2052:
2053: CVSResponseItem newItem = new CVSResponseItem(
2054: CVSResponseItem.SET_CHECKIN_PROG);
2055:
2056: newItem.setPathName(pathName);
2057:
2058: newItem.setAddState(CVSResponseItem.GET_PROGRAM);
2059:
2060: currItem = newItem;
2061: } else if (line.startsWith("Set-update-prog ")) {
2062: String pathName = line.substring(16);
2063: CVSTracer.traceIf(request.traceResponse,
2064: "PARSE: Set-update-prog '" + pathName + "'");
2065:
2066: CVSResponseItem newItem = new CVSResponseItem(
2067: CVSResponseItem.SET_UPDATE_PROG);
2068:
2069: newItem.setPathName(pathName);
2070:
2071: newItem.setAddState(CVSResponseItem.GET_PROGRAM);
2072:
2073: currItem = newItem;
2074: }
2075: // REVIEW should these two be passed to 'responseHandler'?
2076: else if (line.startsWith("E ")) {
2077: // response.appendStdErr( line.substring(2) + "\n" );
2078: if (request.isRedirected()) {
2079: request.redirectLine(line.substring(2));
2080: } else {
2081: response.appendStdErr(line.substring(2) + "\n");
2082: }
2083: } else if (line.startsWith("M ")) {
2084: if (request.isRedirected()) {
2085: request.redirectLine(line.substring(2));
2086: } else {
2087: response.appendStdOut(line.substring(2) + "\n");
2088: }
2089: } else if (line.startsWith("Valid-requests ")) {
2090: CVSResponseItem newItem = new CVSResponseItem(
2091: CVSResponseItem.VALID_REQUESTS);
2092:
2093: newItem.setValidRequests(line.substring(15));
2094:
2095: isok = this .processResponseItem(request, response,
2096: newItem);
2097: } else {
2098: response.appendStdErr("WARNING: stray line:\n '"
2099: + line + "'\n");
2100: }
2101: }
2102:
2103: if (!this .isCanceled()) {
2104: if (gotStatus) {
2105: if (status != CVSResponse.OK) {
2106: response.setStatus(CVSResponse.ERROR);
2107: }
2108: } else {
2109: response
2110: .appendStdErr("\n"
2111: + this .getReason()
2112: + "\nShort response, no status response from server.\n");
2113: response.setStatus(CVSResponse.ERROR);
2114: }
2115: }
2116:
2117: return response;
2118: }
2119:
2120: public boolean retrieveFile(CVSResponseItem item, File file) {
2121: boolean ok = true;
2122: boolean use_gzip = false;
2123:
2124: FileOutputStream out = null;
2125:
2126: int fileSize = 0;
2127: int bytes = 0;
2128: int length;
2129:
2130: String line = null;
2131:
2132: line = this .readLine();
2133:
2134: if (line == null) {
2135: this
2136: .setReason("CVSClient.retrieveFile: ERROR size line is null!");
2137: CVSLog.logMsg(this .getReason());
2138: ok = false;
2139: }
2140:
2141: if (ok) {
2142: if (line.startsWith("z")) {
2143: item.setGZIPed(true);
2144: line = line.substring(1);
2145: }
2146:
2147: try {
2148: fileSize = Integer.valueOf(line).intValue();
2149: } catch (NumberFormatException ex) {
2150: this
2151: .setReason("CVSClient.retrieveFile: ERROR size line is invalid '"
2152: + line + "'");
2153: CVSLog.logMsg(this .getReason());
2154: ok = false;
2155: }
2156: }
2157:
2158: if (CVSClient.LIMIT_FILE_SIZE) {
2159: if (ok) {
2160: if (fileSize > CVSClient.MAX_FILE_SIZE) {
2161: this
2162: .setReason("CVSClient.retrieveFile: ERROR size limit of '"
2163: + CVSClient.MAX_FILE_SIZE
2164: + "' exceeded by '"
2165: + fileSize
2166: + "'");
2167: CVSLog.logMsg(this .getReason());
2168: ok = false;
2169: }
2170: }
2171: }
2172:
2173: if (ok) {
2174: try {
2175: out = new FileOutputStream(file);
2176: } catch (IOException ex) {
2177: this
2178: .setReason("CVSClient.retrieveFile: ERROR opening output file '"
2179: + file.getPath()
2180: + "'\n "
2181: + ex.getMessage());
2182: CVSLog.logMsg(this .getReason());
2183:
2184: // NOTE we do not set 'ok = false;' here, as we need
2185: // to process the downcoming data!!! So, we will have
2186: // to overload with 'out == null' for closure.
2187: }
2188: }
2189:
2190: if (ok) {
2191: int i;
2192: byte[] buffer;
2193: buffer = new byte[8192];
2194:
2195: for (length = fileSize; length > 0;) {
2196: bytes = (length > 8192 ? 8192 : length);
2197:
2198: try {
2199: bytes = this .instream.read(buffer, 0, bytes);
2200: } catch (IOException ex) {
2201: ok = false;
2202: this .setReason("CVSClient.retrieveFile: "
2203: + "ERROR reading file data:\n "
2204: + ex.getMessage());
2205: CVSLog.logMsg(this .getReason());
2206: break;
2207: }
2208:
2209: if (bytes < 0)
2210: break;
2211:
2212: length -= bytes;
2213:
2214: if (out != null) {
2215: try {
2216: out.write(buffer, 0, bytes);
2217: } catch (IOException ex) {
2218: ok = false;
2219: this .setReason("CVSClient.retrieveFile: "
2220: + "ERROR writing output file:\n "
2221: + ex.getMessage());
2222: CVSLog.logMsg(this .getReason());
2223:
2224: try {
2225: out.close();
2226: } catch (IOException ex2) {
2227: }
2228:
2229: out = null;
2230:
2231: break;
2232: }
2233: }
2234:
2235: if (this .isCanceled())
2236: break;
2237: }
2238:
2239: if (out != null) {
2240: // Do NOT set out to null here! See NOTE above.
2241: try {
2242: out.close();
2243: } catch (IOException ex) {
2244: this .setReason("CVSClient.retrieveFile: "
2245: + "ERROR closing output file:\n "
2246: + ex.getMessage());
2247: CVSLog.logMsg(this .getReason());
2248: ok = false;
2249: }
2250: }
2251: }
2252:
2253: // If out is null, an error occurred!
2254: return ((ok && (out != null)) ? true : false);
2255: }
2256:
2257: public boolean sendFileContents(InputStream in) {
2258: int bytes;
2259: boolean result = true;
2260: byte[] buffer = new byte[16 * 1024];
2261:
2262: for (; result;) {
2263: try {
2264: bytes = in.read(buffer, 0, buffer.length);
2265: } catch (IOException ex) {
2266: result = false;
2267: this
2268: .setReason("sendFileRaw: ERROR reading input file: "
2269: + ex.getMessage());
2270: CVSLog.logMsg(this .getReason());
2271: break;
2272: }
2273:
2274: if (bytes < 0)
2275: break;
2276:
2277: try {
2278: this .outstream.write(buffer, 0, bytes);
2279: } catch (IOException ex) {
2280: result = false;
2281: this .setReason("sendFileRaw: ERROR writing file data: "
2282: + ex.getMessage());
2283: CVSLog.logMsg(this .getReason());
2284: break;
2285: }
2286:
2287: if (this .isCanceled())
2288: break;
2289: }
2290:
2291: try {
2292: this .outstream.flush();
2293: } catch (IOException ex) {
2294: result = false;
2295: this
2296: .setReason("sendFileRaw: ERROR flushing server connection: "
2297: + ex.getMessage());
2298: CVSLog.logMsg(this .getReason());
2299: }
2300:
2301: try {
2302: in.close();
2303: } catch (IOException ex) {
2304: result = false;
2305: this .setReason("sendFileRaw: ERROR closing input file: "
2306: + ex.getMessage());
2307: CVSLog.logMsg(this .getReason());
2308: }
2309:
2310: return result;
2311: }
2312:
2313: public boolean sendFileRaw(CVSEntry entry, File entryFile,
2314: boolean useGzipFile) {
2315: int bytes;
2316: Long sizeLong;
2317: long fileSize, length;
2318: boolean result = true;
2319: boolean usingGzip = false;
2320: File gzipFile = null;
2321: BufferedInputStream in = null;
2322: byte[] buffer = new byte[16 * 1024];
2323:
2324: fileSize = entryFile.length();
2325: long beginMillis = System.currentTimeMillis();
2326:
2327: try {
2328: in = new BufferedInputStream(new FileInputStream(entryFile));
2329: } catch (IOException ex) {
2330: result = false;
2331: }
2332:
2333: usingGzip = (useGzipFile && (fileSize > MIN_GZIP_SIZE));
2334:
2335: if (result && usingGzip) {
2336: try {
2337: gzipFile = new File(this .generateTempPath());
2338:
2339: BufferedOutputStream out = new BufferedOutputStream(
2340: new GZIPOutputStream(new FileOutputStream(
2341: gzipFile)));
2342:
2343: for (;;) {
2344: bytes = in.read(buffer, 0, buffer.length);
2345: if (bytes < 0)
2346: break;
2347:
2348: out.write(buffer, 0, bytes);
2349:
2350: if (this .isCanceled())
2351: break;
2352: }
2353:
2354: in.close();
2355: out.close();
2356:
2357: in = new BufferedInputStream(new FileInputStream(
2358: gzipFile));
2359:
2360: fileSize = gzipFile.length();
2361: } catch (IOException ex) {
2362: ex.printStackTrace(System.err);
2363: result = false;
2364: }
2365: }
2366:
2367: long endMillis = System.currentTimeMillis();
2368:
2369: if (false)
2370: System.err.println("CVSClient.sendFileRaw: TIME = '"
2371: + (endMillis - beginMillis) + "' millis.");
2372:
2373: if (result) {
2374: sizeLong = new Long(fileSize);
2375: String sizeStr = sizeLong.toString();
2376:
2377: if (usingGzip)
2378: sizeStr = "z" + sizeStr;
2379:
2380: result = this .sendLine(sizeStr);
2381: if (result) {
2382: result = this .sendFileContents(in);
2383: } else {
2384: this .setReason("sendFileRaw: ERROR writing file size: "
2385: + this .getReason());
2386: CVSLog.logMsg(this .getReason());
2387: }
2388: }
2389:
2390: if (usingGzip && gzipFile != null && gzipFile.exists()) {
2391: try {
2392: gzipFile.delete();
2393: } catch (SecurityException ex) {
2394: // we will leave result, since upload has succeeded...
2395: CVSLog
2396: .logMsg("sendFileRaw: WARNING deleting temp file: "
2397: + ex.getMessage());
2398: }
2399: }
2400:
2401: return result;
2402: }
2403:
2404: public boolean sendFileAscii(CVSEntry entry, File entryFile,
2405: boolean gzipFileMode) {
2406: String inLine;
2407: File tempFile;
2408: BufferedReader in = null;
2409: BufferedOutputStream out = null;
2410: boolean usingGzip = false;
2411: boolean result = true;
2412:
2413: long beginMillis = System.currentTimeMillis();
2414:
2415: try {
2416: in = new BufferedReader(new FileReader(entryFile));
2417: } catch (IOException ex) {
2418: this .setReason("sendFileAscii: can not open input file '"
2419: + entryFile.getPath() + "' " + ex.getMessage());
2420: result = false;
2421: }
2422:
2423: tempFile = new File(this .generateTempPath());
2424:
2425: try {
2426: if (gzipFileMode && (entryFile.length() > MIN_GZIP_SIZE)) {
2427: usingGzip = true;
2428: out = new BufferedOutputStream(new GZIPOutputStream(
2429: new FileOutputStream(tempFile)));
2430: } else {
2431: out = new BufferedOutputStream(new FileOutputStream(
2432: tempFile));
2433: }
2434: } catch (IOException ex) {
2435: result = false;
2436: this .setReason("sendFileAscii: can not open output file '"
2437: + tempFile.getPath() + "' " + ex.getMessage());
2438: CVSLog.logMsg(this .getReason());
2439: }
2440:
2441: if (result) {
2442: for (;;) {
2443: try {
2444: // inLine = in.readLine();
2445: inLine = this .readAsciiLine(in);
2446: if (inLine == null)
2447: break;
2448:
2449: out.write(inLine.getBytes());
2450: out.write('\012');
2451: } catch (IOException ex) {
2452: result = false;
2453: this
2454: .setReason("sendFileAscii: failed converting into temp file '"
2455: + tempFile.getPath()
2456: + "' "
2457: + ex.getMessage());
2458: CVSLog.logMsg(this .getReason());
2459: }
2460:
2461: if (this .isCanceled())
2462: break;
2463: }
2464:
2465: try {
2466: in.close();
2467: } catch (IOException ex) {
2468: result = false;
2469: this
2470: .setReason("sendFileAscii: ERROR closing input file: "
2471: + ex.getMessage());
2472: CVSLog.logMsg(this .getReason());
2473: }
2474: try {
2475: out.close();
2476: } catch (IOException ex) {
2477: result = false;
2478: this
2479: .setReason("sendFileAscii: ERROR closing input file: "
2480: + ex.getMessage());
2481: CVSLog.logMsg(this .getReason());
2482: }
2483:
2484: long endMillis = System.currentTimeMillis();
2485:
2486: if (false)
2487: System.err.println("CVSClient.sendFileAscii: TIME = '"
2488: + (endMillis - beginMillis) + "' millis.");
2489:
2490: if (result) {
2491: Long sizeLong = new Long(tempFile.length());
2492: String sizeStr = sizeLong.toString();
2493:
2494: if (usingGzip)
2495: sizeStr = "z" + sizeStr;
2496:
2497: result = this .sendLine(sizeStr);
2498: if (result) {
2499: try {
2500: BufferedInputStream content = new BufferedInputStream(
2501: new FileInputStream(tempFile));
2502:
2503: result = this .sendFileContents(content);
2504: } catch (FileNotFoundException ex) {
2505: result = false;
2506: this
2507: .setReason("sendFileAscii: failed re-opening temp file '"
2508: + tempFile.getPath()
2509: + "' "
2510: + ex.getMessage());
2511: CVSLog.logMsg(this .getReason());
2512: }
2513: } else {
2514: this
2515: .setReason("sendFileRaw: ERROR writing file size: "
2516: + this .getReason());
2517: CVSLog.logMsg(this .getReason());
2518: }
2519:
2520: try {
2521: tempFile.delete();
2522: } catch (SecurityException ex) {
2523: // we will leave result, since upload has succeeded...
2524: CVSLog
2525: .logMsg("sendFileAscii: WARNING deleting temp file: "
2526: + ex.getMessage());
2527: }
2528: }
2529: }
2530:
2531: return result;
2532: }
2533:
2534: public String readAsciiLine(Reader in) {
2535: String ls = System.getProperty("line.separator");
2536:
2537: int lineSepIdx = 0;
2538: int lineSepLen = ls.length();
2539:
2540: char[] lineSep = new char[lineSepLen];
2541: ls.getChars(0, lineSepLen, lineSep, 0);
2542:
2543: char ch;
2544: StringBuffer line = new StringBuffer(132);
2545:
2546: try {
2547: for (;;) {
2548: int inByte = in.read();
2549: if (inByte == -1) {
2550: // Be sure to grab anything in the lineSep buf...
2551: for (int i = 0; i < lineSepIdx; ++i)
2552: line.append(lineSep[i]);
2553:
2554: if (line.length() == 0)
2555: line = null;
2556:
2557: break;
2558: }
2559:
2560: ch = (char) inByte;
2561: if (ch == lineSep[lineSepIdx]) {
2562: // check for completed line separator.
2563: if (++lineSepIdx >= lineSepLen)
2564: break;
2565: } else {
2566: // Append any buffered separator chars
2567: for (int i = 0; i < lineSepIdx; ++i)
2568: line.append(lineSep[i]);
2569:
2570: // Reset line separator index
2571: lineSepIdx = 0;
2572:
2573: if (ch == lineSep[lineSepIdx]) {
2574: // check for completed line separator.
2575: if (++lineSepIdx >= lineSepLen)
2576: break;
2577: } else {
2578: // Append read char
2579: line.append(ch);
2580: }
2581: }
2582: }
2583: } catch (IOException ex) {
2584: line = null;
2585: }
2586:
2587: return (line != null ? line.toString() : null);
2588: }
2589:
2590: public boolean sendValidResponses(CVSRequest request,
2591: String additional) {
2592: boolean result = true;
2593:
2594: result = this .sendLine("Valid-responses "
2595: + "E M ok error Valid-requests "
2596: + "Created Merged Updated Update-existing "
2597: + "Removed Remove-entry New-entry "
2598: + "Checked-in Checksum Copy-file Notified "
2599: + "Clear-sticky Set-sticky "
2600: + "Clear-static-directory Set-static-directory "
2601: + additional);
2602:
2603: if (request.useUnchanged) {
2604: this .sendLine("UseUnchanged");
2605: }
2606:
2607: return result;
2608: }
2609:
2610: public boolean sendString(String string) {
2611: boolean result = true;
2612:
2613: CVSTracer.traceIf(this .tracingTCPData,
2614: "CVSClient.SENDString: '" + string + "'");
2615:
2616: try {
2617: this .outstream.write(string.getBytes());
2618: this .outstream.flush();
2619: } catch (IOException ex) {
2620: result = false;
2621: }
2622:
2623: return result;
2624: }
2625:
2626: public boolean sendLine(String line) {
2627: boolean result = true;
2628:
2629: CVSTracer.traceIf(this .tracingTCPData, "CVSClient.SENDLine: '"
2630: + line + "'");
2631:
2632: try {
2633: this .outstream.write((line + "\012").getBytes());
2634: this .outstream.flush();
2635: } catch (IOException ex) {
2636: CVSTracer.traceException("SENDLINE: " + line, ex);
2637: result = false;
2638: }
2639:
2640: return result;
2641: }
2642:
2643: public String readLine() {
2644: char ch;
2645: StringBuffer line = new StringBuffer(512); // REVIEW Better number? Avg?
2646:
2647: try {
2648: for (;;) {
2649: int inByte = this .instream.read();
2650: if (inByte == -1) {
2651: if (line.length() == 0)
2652: line = null;
2653: break;
2654: }
2655:
2656: ch = (char) inByte;
2657: if (ch == '\012') {
2658: break;
2659: }
2660:
2661: line.append(ch);
2662: }
2663: } catch (IOException ex) {
2664: line = null;
2665: }
2666:
2667: CVSTracer.traceIf(this .tracingTCPData, "CVSClient.READLine: '"
2668: + ((line == null) ? "(null)" : line.toString()) + "'");
2669:
2670: return (line != null ? line.toString() : null);
2671: }
2672:
2673: public String readResponse() {
2674: String line;
2675: StringBuffer result = new StringBuffer("");
2676:
2677: for (;;) {
2678: line = this .readLine();
2679:
2680: if (line == null)
2681: break;
2682:
2683: CVSTracer.traceIf(this .tracingTCPData,
2684: "CVSClient.READLine: '" + line + "'");
2685:
2686: result.append(line);
2687: result.append("\n");
2688:
2689: if (line.startsWith("ok")) {
2690: break;
2691: }
2692:
2693: if (line.startsWith("error")) {
2694: break;
2695: }
2696: }
2697:
2698: return result.toString();
2699: }
2700:
2701: private InetAddress getInterfaceAddress(String host, int port)
2702: throws IOException {
2703: InetAddress interfaceAddress = null;
2704:
2705: //
2706: // The following code is used for the case where we have
2707: // multiple interfaces on the system. When there is more
2708: // than one interface, InetAddress.getLocalHost() may not
2709: // return the interface that we need to route to the cvs
2710: // server. Thus, we instead first make a connection, then
2711: // we use that connection to determine the interface, and
2712: // ergo the ip address to use, that routes to the server.
2713: //
2714: // REVIEW
2715: // This does cost us a double connect, which is expensive.
2716: // I am also not sure what cost it is to the server, which
2717: // will reject the rsh because it is not on a priveleged
2718: // port. We may eventually want a property to be able to
2719: // turn this on and off.
2720: //
2721: // Thanks to Roger Vaughn <rvaughn@pobox.com> for solving
2722: // this problem and providing this code!
2723: //
2724:
2725: try {
2726: Socket probe = new Socket(host, port);
2727: interfaceAddress = probe.getLocalAddress();
2728: probe.close();
2729: } catch (IOException ex) {
2730: // Didn't work! Use the "default" instead.
2731: interfaceAddress = InetAddress.getLocalHost();
2732: }
2733:
2734: return interfaceAddress;
2735: }
2736:
2737: private Socket bindLocalSocket(CVSRequest request,
2738: InetAddress localhost, String host, int port)
2739: throws IOException {
2740: Socket sock = null;
2741: String errMessage = "could not bind socket between 1025-1152 locally";
2742:
2743: for (int local = 1025; sock == null && local < 1152; ++local) {
2744: if (this .isCanceled())
2745: break;
2746:
2747: CVSTracer.traceIf(request.traceRequest,
2748: "bindLocalSocket() trying port " + local);
2749:
2750: try {
2751: sock = new Socket(host, port, localhost, local);
2752: } catch (IOException ex) {
2753: socket = null;
2754: CVSTracer.traceIf(request.traceRequest,
2755: "bindLocalSocket() exception message: "
2756: + ex.getMessage());
2757:
2758: //
2759: // HACK
2760: // If we attempt to run through each of these sockets, while
2761: // we are going over the wire (we don't get "Address in use",
2762: // we get "Connection refused"), then we are going to spend
2763: // one hell of a time in this loop. The hack is that we depend
2764: // on the word "refused" to be in the correct error message. If
2765: // not, then we only revert back to the infinite wait while we
2766: // loop over all of these ports.
2767: //
2768: if (ex.getMessage().indexOf("refused") > -1
2769: || ex.getMessage().indexOf("timed out") > -1) {
2770: errMessage = ex.getMessage();
2771: break;
2772: }
2773: }
2774: }
2775:
2776: if (sock == null) {
2777: throw new IOException(errMessage);
2778: }
2779:
2780: return sock;
2781: }
2782:
2783: //
2784: // UNDONE HIGH
2785: // There *is* a .ssh_known_hosts file, especially w/ cygwin.
2786: //
2787: public boolean verifyHost(String host, SshPublicKey pk)
2788: throws TransportProtocolException {
2789: CVSTracer.traceIf(false, "CVSClient.verifyHost: host '" + host
2790: + "', Pk '" + pk + "'");
2791:
2792: return true;
2793: }
2794:
2795: private void establishSSHConnection(CVSRequest request)
2796: throws IOException {
2797: SshConnectionProperties properties = new SshConnectionProperties();
2798:
2799: CVSTracer
2800: .traceIf(request.traceRequest,
2801: "CVSClient.establishSSHConnection: creating connection...");
2802:
2803: InetAddress localhost = this .getInterfaceAddress(request
2804: .getHostName(), request.getPort());
2805:
2806: properties.setHost(request.getHostName());
2807: properties.setPort(request.getPort());
2808:
2809: CVSTracer.traceIf(request.traceRequest,
2810: "CVSClient.establishSSHConnection: localHost="
2811: + localhost);
2812:
2813: this .sshClient = new SshClient();
2814:
2815: CVSTracer.traceIf(request.traceRequest,
2816: "CVSClient.establishSSHConnection: sshClient="
2817: + this .sshClient);
2818:
2819: this .sshClient.connect(properties, this );
2820:
2821: CVSTracer.traceIf(request.traceRequest,
2822: "CVSClient.establishSSHConnection: connected");
2823:
2824: PasswordAuthenticationClient pwdAuth = new PasswordAuthenticationClient();
2825:
2826: pwdAuth.setUsername(request.getUserName());
2827: pwdAuth.setPassword(request.getPassword());
2828:
2829: int result = sshClient.authenticate(pwdAuth);
2830:
2831: CVSTracer.traceIf(request.traceRequest,
2832: "CVSClient.authenticate: result=" + result);
2833:
2834: if (result == AuthenticationProtocolState.FAILED) {
2835: CVSTracer.traceIf(request.traceRequest,
2836: "The authentication failed");
2837: throw new IOException("ssh authentication failure");
2838: } else if (result == AuthenticationProtocolState.PARTIAL) {
2839: CVSTracer.traceIf(request.traceRequest,
2840: "The authentication succeeded but another"
2841: + "authentication is required");
2842: throw new IOException("ssh authentication partial");
2843: } else if (result == AuthenticationProtocolState.COMPLETE) {
2844: CVSTracer.traceIf(request.traceRequest,
2845: "The authentication is complete");
2846: }
2847:
2848: // String srvVersionStr = this.sshTransport.getServerVersion();
2849:
2850: // CVSTracer.traceIf( request.traceRequest,
2851: // "CVSClient.establishSSHConnection: SVR VERSION '" + srvVersionStr + "'" );
2852:
2853: this .sshSession = this .sshClient.openSessionChannel();
2854:
2855: CVSTracer.traceIf(request.traceRequest,
2856: "CVSClient.establishSSHConnection: sshSession="
2857: + this .sshSession);
2858:
2859: boolean ok = this .sshSession.executeCommand(request
2860: .getServerCommand());
2861:
2862: CVSTracer.traceIf(request.traceRequest,
2863: "CVSClient.establishSSHConnection: command("
2864: + request.getServerCommand() + ") = " + ok);
2865:
2866: if (ok) {
2867: CVSTracer
2868: .traceIf(request.traceRequest,
2869: "CVSClient.establishSSHConnection: command session established");
2870: } else {
2871: CVSTracer.traceIf(request.traceRequest,
2872: "CVSClient.establishSSHConnection: executeCommand( '"
2873: + request.getServerCommand() + "' failed.");
2874:
2875: throw new IOException("failed to execute command '"
2876: + request.getServerCommand() + "'");
2877: }
2878: }
2879:
2880: private Socket establishRSHSocket(CVSRequest request)
2881: throws IOException {
2882: Socket sock = null;
2883:
2884: InetAddress localhost = this .getInterfaceAddress(request
2885: .getHostName(), request.getPort());
2886:
2887: for (int local = 512; sock == null && local < 1024; ++local) {
2888: try {
2889: sock = new Socket(request.getHostName(), request
2890: .getPort(), localhost, local);
2891: } catch (IOException ex) {
2892: socket = null;
2893: }
2894: }
2895:
2896: if (sock == null) {
2897: throw new IOException(
2898: "Could not bind rsh socket between 512-1023 locally.");
2899: }
2900:
2901: return sock;
2902: }
2903:
2904: private boolean openServer(CVSRequest request) {
2905: boolean result;
2906:
2907: this .socket = null;
2908: this .process = null;
2909: this .serverIsOpen = false;
2910:
2911: // Create the socket used for the event server connection
2912: try {
2913: CVSTracer.traceIf(request.traceRequest,
2914: "CVSClient.openServer: creating connection...");
2915:
2916: result = true;
2917: switch (request.getConnectionMethod()) {
2918: case CVSRequest.METHOD_INETD:
2919: this .socket = new Socket(request.getHostName(), request
2920: .getPort());
2921: break;
2922:
2923: case CVSRequest.METHOD_SSH:
2924: this .establishSSHConnection(request);
2925: break;
2926:
2927: case CVSRequest.METHOD_RSH:
2928: if (request.getRshProcess() != null) {
2929: int index = request.getServerCommand().indexOf(' ');
2930:
2931: String[] argv = new String[6];
2932:
2933: argv[0] = request.getRshProcess();
2934: argv[1] = request.getHostName();
2935: argv[2] = "-l";
2936: argv[3] = request.getUserName();
2937: if (index < 0) {
2938: argv[4] = request.getServerCommand();
2939: argv[5] = "server";
2940: } else {
2941: argv[4] = request.getServerCommand().substring(
2942: 0, index);
2943: argv[5] = request.getServerCommand().substring(
2944: index + 1);
2945: }
2946:
2947: if (request.traceRequest) {
2948: for (int i = 0; i < argv.length; ++i)
2949: CVSTracer.traceIf(true,
2950: "CVSClient.openServer: RSH argv["
2951: + i + "] = '" + argv[i]
2952: + "'");
2953: }
2954:
2955: this .process = Runtime.getRuntime().exec(argv);
2956: } else {
2957: this .socket = this .establishRSHSocket(request);
2958: }
2959: break;
2960: }
2961:
2962: CVSTracer.traceIf(request.traceRequest,
2963: "CVSClient.openServer: creating i/o streams...");
2964:
2965: if (this .sshSession != null) {
2966: this .instream = new DataInputStream(this .sshSession
2967: .getInputStream());
2968:
2969: this .outstream = new DataOutputStream(this .sshSession
2970: .getOutputStream());
2971: } else if (this .process != null) {
2972: this .instream = new DataInputStream(this .process
2973: .getInputStream());
2974:
2975: this .outstream = new DataOutputStream(this .process
2976: .getOutputStream());
2977: } else if (this .socket != null) {
2978: this .instream = new DataInputStream(this .socket
2979: .getInputStream());
2980:
2981: this .outstream = new DataOutputStream(this .socket
2982: .getOutputStream());
2983: } else {
2984: CVSTracer
2985: .traceIf(request.traceRequest,
2986: "CVSClient.openServer: failed to establish connection.");
2987: result = false;
2988: }
2989:
2990: if (result) {
2991: CVSTracer.traceIf(request.traceRequest,
2992: "CVSClient.openServer: server is open.");
2993: this .serverIsOpen = true;
2994: }
2995: } catch (IOException ex) {
2996: this .serverIsOpen = false;
2997: int meth = request.getConnectionMethod();
2998:
2999: this .setReason("could not create "
3000: + (meth == CVSRequest.METHOD_INETD ? "INETD"
3001: : (meth == CVSRequest.METHOD_RSH ? "RSH"
3002: : "SSH")) + " connection for '"
3003: + port + "@" + request.getHostName() + "' --> "
3004: + ex.getMessage());
3005:
3006: CVSLog.logMsg(this .getReason());
3007: }
3008:
3009: if (this .serverIsOpen
3010: && this .socket != null
3011: && (request.getConnectionMethod() == CVSRequest.METHOD_RSH)) {
3012: CVSTracer
3013: .traceIf(request.traceRequest,
3014: "CVSClient.openServer: performing rsh protocol initialization.");
3015:
3016: result = this .performRSHProtocol(request.getUserName(),
3017: request.getServerCommand());
3018:
3019: if (result == false) {
3020: this .closeServer();
3021: }
3022: }
3023:
3024: return this .serverIsOpen;
3025: }
3026:
3027: public boolean performRSHProtocol(String remoteUserName,
3028: String serverCommand) {
3029: int status;
3030:
3031: // ------------- STDERR PORT ---------------
3032: try {
3033: this .outstream.write(48); // 48 = "0"
3034: this .outstream.write(0);
3035: } catch (IOException ex) {
3036: CVSLog.logMsg("RSH: FAIL-ed writing stderr port");
3037: return false;
3038: }
3039:
3040: // ------------- LOCAL USER NAME ---------------
3041: // Use the Java system property "user.name" for the local
3042: // name, but default to remoteUserName is not set.
3043: String localUserName = System.getProperty("user.name",
3044: remoteUserName);
3045:
3046: try {
3047: this .outstream.write(localUserName.getBytes());
3048: this .outstream.write(0);
3049: } catch (IOException ex) {
3050: CVSLog.logMsg("RSH: FAIL-ed writing local user");
3051: return false;
3052: }
3053:
3054: // ------------- REMOTE USER NAME ---------------
3055: try {
3056: this .outstream.write(remoteUserName.getBytes());
3057: this .outstream.write(0);
3058: } catch (IOException ex) {
3059: CVSLog.logMsg("RSH: FAIL-ed writing remote user");
3060: return false;
3061: }
3062:
3063: // ------------- COMMAND LINE ---------------
3064: try {
3065: this .outstream.write(serverCommand.getBytes());
3066: this .outstream.write(0);
3067: this .outstream.flush();
3068: } catch (IOException ex) {
3069: CVSLog.logMsg("RSH: FAIL-ed writing command");
3070: return false;
3071: }
3072:
3073: // ------------- READ STATUS BYTE ---------------
3074: try {
3075: status = this .instream.read();
3076: } catch (IOException ex) {
3077: CVSLog.logMsg("RSH: FAIL-ed reading status");
3078: return false;
3079: }
3080:
3081: if (status != 0)
3082: return false;
3083:
3084: return true;
3085: }
3086:
3087: public boolean closeServer() {
3088: boolean result = true;
3089:
3090: if (this .serverIsOpen) {
3091: try {
3092: if (this .sshSession != null) {
3093: this .sshSession.close();
3094: this .sshClient.disconnect();
3095: } else {
3096: this .instream.close();
3097: this .outstream.close();
3098: }
3099:
3100: if (this .socket != null) {
3101: this .socket.close();
3102: } else if (this .process != null) {
3103: this .process.destroy();
3104: }
3105: } catch (IOException ex) {
3106: result = false;
3107: this .setReason("could not close socket - "
3108: + ex.getMessage());
3109: CVSLog.logMsg(this .getReason());
3110: }
3111:
3112: this .socket = null;
3113: this .sshClient = null;
3114: this .sshSession = null;
3115: this .instream = null;
3116: this .outstream = null;
3117: this .serverIsOpen = false;
3118: }
3119:
3120: return result;
3121: }
3122:
3123: // EH-null-ui Etienne-Hugues Fortin <ehfortin@sympatico.ca>
3124: private class NullCVSUI implements CVSUserInterface {
3125: public void uiDisplayProgressMsg(String message) {
3126: }
3127:
3128: public void uiDisplayProgramError(String error) {
3129: }
3130:
3131: public void uiDisplayResponse(CVSResponse response) {
3132: }
3133: }
3134:
3135: }
|