1 /*****************************************************************************/
2 /* Software Testing Automation Framework (STAF)                              */
3 /* (C) Copyright IBM Corp. 2001, 2004, 2005                                  */
4 /*                                                                           */
5 /* This software is licensed under the Eclipse Public License (EPL) V1.0.    */
6 /*****************************************************************************/
7 
8 #include "STAF.h"
9 #include "STAF_iostream.h"
10 #include "STAF_fstream.h"
11 #include "STAFProc.h"
12 #include "STAFProcUtil.h"
13 #include "STAFString.h"
14 #include "STAFRefPtr.h"
15 #include "STAFUtil.h"
16 #include "STAFFSService.h"
17 #include "STAFConnectionManager.h"
18 #include "STAFVariablePool.h"
19 #include "STAFException.h"
20 #include "STAFFileSystem.h"
21 #include "STAFHandleManager.h"
22 #include "STAFFSCopyManager.h"
23 #include "STAFTrace.h"
24 #include "STAFConverter.h"
25 #include "STAFThreadManager.h"
26 #include "STAFDataTypes.h"
27 #include <set>
28 #include <cstring>
29 #include <deque>
30 #include <algorithm>
31 
32 #ifdef STAF_USE_SSL
33 #include <openssl/evp.h>
34 #include <openssl/crypto.h>
35 #endif
36 
37 typedef std::set<STAFString> SET_STAFString;
38 typedef std::deque<STAFFSEntryPtr> STAFFSEntryList;
39 
40 static STAFMutexSem sStrictFSCopyTrustSem;
41 static const STAFString sEnabled = "Enabled";
42 static const STAFString sDisabled = "Disabled";
43 static const unsigned int sMaxReadAttempts = 20;
44 static const unsigned int sReadRetryDelay = 500;  // 1/2 second (500ms)
45 static const STAFString sPeriod(kUTF8_PERIOD);
46 static const STAFString sDoublePeriod(sPeriod + sPeriod);
47 static const STAFString sNoneString("<None>");
48 static const STAFString sStar(kUTF8_STAR);
49 static const STAFString sDoubleQuote(kUTF8_DQUOTE);
50 static const STAFString sSpace(kUTF8_SPACE);
51 static STAFString sHelpMsg;
52 
53 // Line ending strings for Windows and Unix
54 static const STAFString sWindowsEOL = STAFString(kUTF8_CR) +
55                                       STAFString(kUTF8_LF);
56 static const STAFString sUnixEOL = STAFString(kUTF8_LF);
57 
58 // Maximum buffer size for searching for a line ending in a file
59 static const int sLineEndingBufferSize = 4000;
60 
61 static STAFMapClassDefinitionPtr fListLongInfoClass;
62 static STAFMapClassDefinitionPtr fListDetailsInfoClass;
63 static STAFMapClassDefinitionPtr fListSummaryInfoClass;
64 
65 // This table allows for lookup of a hex character (in UTF-8)
66 static const char HEX_TABLE[] =
67 {
68     // Here's the corresponding non-UTF-8 hex representation
69     // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
70     // 'A', 'B', 'C', 'D', 'E', 'F'
71 
72     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
73     0x41, 0x42, 0x43, 0x44, 0x45, 0x46
74 };
75 
76 #ifdef STAF_USE_SSL
77 // The OpenSSL FAQ says: "Multi-threaded applications must provide two
78 // callback functions to OpenSSL by calling CRYPTO_set_locking_callback()
79 // and CRYPTO_set_id_callback().  Since the FS service's GET ENTRY CHECKSUM
80 // request calls OpenSSL apis, the FS service must provide these OpenSSL
81 // callback functions.
82 
83 // This is a pointer to an array of mutexes needed by the OpenSSL locking
84 // callback function
85 #ifdef STAF_OS_TYPE_WIN32
86 static HANDLE *lock_cs;
87 #else
88 static pthread_mutex_t *lock_cs;
89 #endif
90 
91 /*
92  * The locking callback function is needed to perform locking on shared data
93  * structures used by OpenSSL whenever multiple threads use OpenSSL.
94  * This function must be able to handle up to CRYPTO_num_locks() different
95  * mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and releases it
96  * otherwise.  We define one locking callback function for Windows and another
97  * for Unix operating systems.
98  */
99 #ifdef STAF_OS_TYPE_WIN32
STAF_SSL_Locking_Callback(int mode,int type,const char * file,int line)100 static void STAF_SSL_Locking_Callback(int mode, int type,
101                                       const char *file, int line)
102 {
103     if (mode & CRYPTO_LOCK)
104         WaitForSingleObject(lock_cs[type], INFINITE);
105     else
106         ReleaseMutex(lock_cs[type]);
107 }
108 
109 #else
STAF_SSL_Locking_Callback(int mode,int type,const char * file,int line)110 static void STAF_SSL_Locking_Callback(int mode, int type,
111                                       const char *file, int line)
112 {
113     if (mode & CRYPTO_LOCK)
114         pthread_mutex_lock(&(lock_cs[type]));
115     else
116         pthread_mutex_unlock(&(lock_cs[type]));
117 }
118 
119 /*
120  * This callback function returns a thread ID.  It isn't needed on Windows
121  * nor on platforms where getpid() returns a different ID for each thread.
122  */
STAF_SSL_ThreadID_Callback()123 static unsigned long STAF_SSL_ThreadID_Callback()
124 {
125     return (unsigned long)pthread_self();
126 }
127 #endif
128 
129 /*
130  * This function assigns the callback functions needed for multi-threaded
131  * applications that use OpenSSL so that they don't randomly crash.
132  */
STAF_SSL_Thread_Setup()133 static void STAF_SSL_Thread_Setup()
134 {
135     // The CRYPTO_set_locking_callback() method requires an array of mutexes
136     // that is needed for locking access to global shared data structure
137     // used by OpenSSL
138 
139 #   ifdef STAF_OS_TYPE_WIN32
140         lock_cs = (HANDLE *)OPENSSL_malloc(
141             CRYPTO_num_locks() * sizeof(HANDLE));
142 #   else
143         lock_cs = (pthread_mutex_t *)OPENSSL_malloc(
144             CRYPTO_num_locks() * sizeof(pthread_mutex_t));
145 #   endif
146 
147     for (int i = 0; i < CRYPTO_num_locks(); i++)
148     {
149 #       ifdef STAF_OS_TYPE_WIN32
150             lock_cs[i] = CreateMutex(NULL, FALSE, NULL);
151 #       else
152             pthread_mutex_init(&(lock_cs[i]), NULL);
153 #       endif
154     }
155 
156     // Assign callback functions needed for multi-threaded applications
157     // that use OpenSSL
158 
159     CRYPTO_set_locking_callback(STAF_SSL_Locking_Callback);
160 
161     // If Unix, assign a callback function for returning a thread id
162 
163 #   ifndef STAF_OS_TYPE_WIN32
164         CRYPTO_set_id_callback(STAF_SSL_ThreadID_Callback);
165 #   endif
166 }
167 
STAF_SSL_Thread_Cleanup()168 static void STAF_SSL_Thread_Cleanup()
169 {
170     // Remove the SSL locking callback function
171 
172     CRYPTO_set_locking_callback(NULL);
173 
174     // If Unix, Remove the SSL thread id callback function
175 
176 #   ifndef STAF_OS_TYPE_WIN32
177         CRYPTO_set_id_callback(NULL);
178 #   endif
179 
180     // Delete the mutexes used for locking by OpenSSL
181 
182     for (int i = 0; i < CRYPTO_num_locks(); i++)
183     {
184 #       ifdef STAF_OS_TYPE_WIN32
185             CloseHandle(lock_cs[i]);
186 #       else
187             pthread_mutex_destroy(&(lock_cs[i]));
188 #       endif
189     }
190 
191     OPENSSL_free(lock_cs);
192 }
193 #endif
194 
195 struct STAFSortEnumByName
196 {
STAFSortEnumByNameSTAFSortEnumByName197     STAFSortEnumByName(STAFFSCaseSensitive_t caseSensitive)
198         : fCaseSensitive(caseSensitive)
199     { /* Do nothing */ }
200 
operator ()STAFSortEnumByName201     bool operator()(STAFFSEntryPtr lhs, STAFFSEntryPtr rhs)
202     {
203         unsigned int comp = 0;
204 
205         if (fCaseSensitive == kSTAFFSCaseSensitive)
206         {
207             STAFStringCompareTo(lhs->path().asString().getImpl(),
208                                 rhs->path().asString().getImpl(), &comp, 0);
209         }
210         else
211         {
212             STAFStringCompareTo(lhs->path().asString().toUpperCase().getImpl(),
213                                 rhs->path().asString().toUpperCase().getImpl(),
214                                 &comp, 0);
215         }
216 
217         return (comp == 1);
218     }
219 
220     STAFFSCaseSensitive_t fCaseSensitive;
221 };
222 
223 
sortEnumBySize(STAFFSEntryPtr lhs,STAFFSEntryPtr rhs)224 static bool sortEnumBySize(STAFFSEntryPtr lhs, STAFFSEntryPtr rhs)
225 {
226     return (lhs->size64() < rhs->size64());
227 }
228 
229 
sortEnumByModTime(STAFFSEntryPtr lhs,STAFFSEntryPtr rhs)230 static bool sortEnumByModTime(STAFFSEntryPtr lhs, STAFFSEntryPtr rhs)
231 {
232     return (lhs->modTime() < rhs->modTime());
233 }
234 
235 
getTypeString(STAFFSEntryType_t type)236 STAFString getTypeString(STAFFSEntryType_t type)
237 {
238     switch (type)
239     {
240         case kSTAFFSFile:              return "F";
241         case kSTAFFSSpecialDirectory: // Fall through
242         case kSTAFFSDirectory:         return "D";
243         case kSTAFFSPipe:              return "P";
244         case kSTAFFSSocket:            return "S";
245         case kSTAFFSSymLink:           return "L";
246         case kSTAFFSBlkDev:            return "B";
247         case kSTAFFSCharDev:           return "C";
248         case kSTAFFSOther:             return "O";
249         default:                       return "?";
250     }
251 }
252 
getTypeMaskFromString(const STAFString & typeString,bool includeSpecial)253 unsigned int getTypeMaskFromString(const STAFString &typeString,
254                                    bool includeSpecial)
255 {
256     STAFString upperTypeString = typeString.toUpperCase();
257     unsigned int entryTypesUInt = kSTAFFSNone;
258 
259     for (int i = 0; i < upperTypeString.length(STAFString::kChar); ++i)
260     {
261         STAFString thisChar = upperTypeString.subString(i, 1, STAFString::kChar);
262 
263         if ((thisChar == "!") && includeSpecial)
264         {
265             entryTypesUInt |= kSTAFFSSpecialDirectory;
266         }
267         else if (thisChar == "F") entryTypesUInt |= kSTAFFSFile;
268         else if (thisChar == "D") entryTypesUInt |= kSTAFFSDirectory;
269         else if (thisChar == "P") entryTypesUInt |= kSTAFFSPipe;
270         else if (thisChar == "S") entryTypesUInt |= kSTAFFSSocket;
271         else if (thisChar == "L") entryTypesUInt |= kSTAFFSSymLink;
272         else if (thisChar == "B") entryTypesUInt |= kSTAFFSBlkDev;
273         else if (thisChar == "C") entryTypesUInt |= kSTAFFSCharDev;
274         else if (thisChar == "O") entryTypesUInt |= kSTAFFSOther;
275     }
276 
277     return entryTypesUInt;
278 }
279 
280 
updateResultString(STAFObjectPtr & outputList,STAFFSEntryPtr & entry,STAFRC_t rc,unsigned int osRC)281 void updateResultString(STAFObjectPtr &outputList, STAFFSEntryPtr &entry,
282                         STAFRC_t rc, unsigned int osRC)
283 {
284     if (rc != kSTAFOk)
285     {
286         STAFObjectPtr errorInfoMap = STAFObject::createMap();
287         errorInfoMap->put("staf-map-class-name",
288                           "STAF/Service/FS/ErrorInfo");
289         errorInfoMap->put("name", entry->path().asString());
290         errorInfoMap->put("rc", STAFString(rc));
291 
292         if (rc == kSTAFBaseOSError)
293             errorInfoMap->put("osRC", STAFString(osRC));
294 
295         // Add remaining map entries here
296         outputList->append(errorInfoMap);
297     }
298 }
299 
300 
readFile(fstream & inFile,char * fileBuffer,unsigned int readLength,const STAFString fromFile,const STAFString toMachine,unsigned int fileLength,unsigned int currentPos)301 STAFRC_t readFile(fstream &inFile, char *fileBuffer, unsigned int readLength,
302                   const STAFString fromFile, const STAFString toMachine,
303                   unsigned int fileLength, unsigned int currentPos)
304 {
305     /* Leave in (commented out) for future debugging purposes.
306     // Make sure that currentPos is the correct current position in the file
307     // (indicating the number of bytes read so far) now that we're no longer
308     // using tellg() because it only returns an int which means it does not
309     // support files >= 2G.
310     if (currentPos <= (INT_MAX - readLength))
311     {
312         int currentPosInt = inFile.tellg();// Save current position before read
313         unsigned int myCurrentPos = (unsigned int)currentPosInt;
314 
315         if (myCurrentPos != currentPos)
316         {
317             cout << "STAFFSService::readFile() - MISMATCH: myCurrentPos="
318                  << myCurrentPos << " currentPos=" << currentPos << endl;
319         }
320     }
321     */
322 
323     // Read a block of data from the file
324 
325     inFile.read(fileBuffer, readLength);
326 
327     if (inFile.good())
328         return kSTAFOk;  // Read was successful
329 
330     // If get eof condition reading the file, make sure that all bytes have
331     // really been read to make sure it's not a "pre-mature" eof condition.
332     //
333     // Note:  A "pre-mature" eof condition can occur on Windows when copying
334     // from a file on a mapped drive (in text mode) and the drive is
335     // disconnected.
336 
337     if (inFile.eof() && (currentPos + inFile.gcount() == fileLength))
338         return kSTAFOk;  // Completed reading the file
339 
340     // The read failed.  Retry the read up to sMaxReadAttempts times with a
341     // delay between each attempt.
342 
343     for (int readAttempt = 1;
344          !inFile.good() && readAttempt <= sMaxReadAttempts;
345          readAttempt++)
346     {
347         if (inFile.fail())
348         {
349             // Recoverable read error
350 
351             // Log a warning tracepoint message
352 
353             STAFString warningMsg(
354                 "STAFFSService::readFile() - Read attempt #" +
355                 STAFString(readAttempt) + " failed while copying file " +
356                 fromFile + " to machine " + toMachine + " after reading " +
357                 STAFString(currentPos) + " bytes");
358 
359             STAFTrace::trace(kSTAFTraceWarning, warningMsg);
360 
361             // Delay and retry read after clearing any error flags and
362             // repositioning the file pointer
363 
364             STAFThreadManager::sleepCurrentThread(
365                 sReadRetryDelay);
366 
367             inFile.clear();
368 
369             if (readAttempt == sMaxReadAttempts)
370             {
371                 // Before the last read attempt, try closing the file and
372                 // reopening it first to see if that fixes the problem
373 
374                 inFile.close();
375                 inFile.open(fromFile.toCurrentCodePage()->buffer(),
376                             ios::in | STAF_ios_binary);
377             }
378 
379             if (currentPos <= INT_MAX)
380             {
381                 inFile.seekg(currentPos, ios::beg);
382                 inFile.read(fileBuffer, readLength);
383             }
384             else
385             {
386                 // Reading a large file, so need to do multiple seekg()'s
387                 // because seekg() only accepts an integer for the offset
388 
389                 inFile.seekg(INT_MAX, ios::beg);
390 
391                 unsigned int setPos = currentPos - INT_MAX;
392 
393                 while (setPos > INT_MAX)
394                 {
395                     inFile.seekg(INT_MAX, ios::cur);
396                     setPos = setPos - INT_MAX;
397                 }
398 
399                 inFile.seekg((int)setPos, ios::cur);
400 
401                 if (inFile.good())
402                     inFile.read(fileBuffer, readLength);
403             }
404         }
405         else if (inFile.bad())
406         {
407             // Unrecoverable read error.
408             break;
409         }
410     }
411 
412     if (!inFile.good())
413     {
414         // Unrecoverable read failure
415 
416         return kSTAFFileReadError;
417     }
418 
419     return kSTAFOk;
420 }
421 
422 
determineLineEnding(unsigned int fileLength,fstream & inFile,const STAFString & fromFile,const STAFString & toMachine,const STAFString & defaultEOL)423 STAFServiceResult determineLineEnding(unsigned int fileLength,
424                                       fstream &inFile,
425                                       const STAFString &fromFile,
426                                       const STAFString &toMachine,
427                                       const STAFString &defaultEOL)
428 {
429     // Determine current EOL (End Of Line ending) as follows:
430     // 1) Look for the first Windows line ending and the first Unix
431     //    line ending in the first 4000 characters of the source file.
432     //    Whichever line ending is found first will be assumed to be
433     //    the line ending in this text file.
434     // 2) If no Windows or Unix line ending character is found in the
435     //    first 4000 characters, then default to the line ending
436     //    character for the type of operating system where the source
437     //    source file resides.
438     //
439     // If successful, the current EOL will be returned in the fResult
440     // of the STAFServiceResult returned.
441 
442     STAFString currentEOL = STAFString("");
443 
444     if (fileLength > 0)
445     {
446         char *buffer = new char[sLineEndingBufferSize];
447         unsigned int readLength = STAF_MIN(sLineEndingBufferSize, fileLength);
448 
449         STAFRC_t rc = readFile(inFile, buffer, readLength,
450                                fromFile, toMachine, fileLength, 0);
451 
452         if (rc == kSTAFOk)
453         {
454             // Create a STAFString containing up to the first 4000 bytes
455             // of the file data
456 
457             try
458             {
459                 STAFString fileData = STAFString(buffer, readLength);
460 
461                 // Check which line ending is found first in the file and
462                 // assign to currentEOL
463 
464                 unsigned int windowsEOLIndex = fileData.find(sWindowsEOL);
465                 unsigned int unixEOLIndex = fileData.find(sUnixEOL);
466 
467                 if (windowsEOLIndex < unixEOLIndex)
468                     currentEOL = sWindowsEOL;
469                 else if (unixEOLIndex < windowsEOLIndex)
470                     currentEOL = sUnixEOL;
471             }
472             catch (...)
473             {
474                 // Do nothing
475             }
476         }
477 
478         // Set the file pointer back to the beginning of the file
479 
480         inFile.clear();
481         inFile.seekg(0, ios::beg);
482     }
483 
484     if (currentEOL.length() == 0)
485     {
486         // Could not determine the current EOL by looking for the first EOL in
487         // the first 4000 bytes, so set to source machine's OS line ending.
488 
489         if (defaultEOL.length() > 0)
490         {
491             // On a directory copy, the line ending for the source machine
492             // has already been determined and is passed in as defaultEOL
493 
494             currentEOL = defaultEOL;
495         }
496         else
497         {
498             STAFConfigInfo sysInfo;
499             STAFString_t errorBuffer;
500             unsigned int osRC;
501 
502             if (STAFUtilGetConfigInfo(&sysInfo, &errorBuffer, &osRC) !=
503                 kSTAFOk)
504             {
505                 return STAFServiceResult(
506                     kSTAFBaseOSError,
507                     STAFString("Get current EOL failure.  ") +
508                     STAFString(errorBuffer, STAFString::kShallow) +
509                     ", RC: " + osRC);
510             }
511 
512             currentEOL = sysInfo.lineSeparator;
513         }
514     }
515 
516     return STAFServiceResult(kSTAFOk, currentEOL);
517 }
518 
copyDirectory(STAFFSEnumPtr childEnum,STAFString fromDir,STAFConnectionPtr connection,SET_STAFString textExtList,STAFString currentEOL,STAFString newEOL,bool doCodepageConvert,int level,STAFFSCaseSensitive_t caseSensitive,STAFObjectPtr & outputList,STAFFSCopyManager::FSCopyDataPtr copyDataPtr,const STAFString & toMachine)519 STAFRC_t copyDirectory(STAFFSEnumPtr childEnum, STAFString fromDir,
520     STAFConnectionPtr connection, SET_STAFString textExtList,
521     STAFString currentEOL, STAFString newEOL, bool doCodepageConvert,
522     int level, STAFFSCaseSensitive_t caseSensitive, STAFObjectPtr &outputList,
523     STAFFSCopyManager::FSCopyDataPtr copyDataPtr, const STAFString &toMachine)
524 {
525     unsigned int osRC = 0;
526     STAFRC_t rc = kSTAFOk;
527     STAFRC_t ack = kSTAFOk;
528     STAFString ackResult;
529     STAFFSEntryPtr fileEntry;
530     STAFString toFile;
531     bool textTransfer;
532     bool doConvert = doCodepageConvert;
533     SET_STAFString::iterator i;
534 
535     for (; childEnum->isValid(); childEnum->next())
536     {
537         fileEntry = childEnum->entry();
538         STAFString fromFile = fileEntry->path().asString();
539         fromFile = fromFile.replace(kUTF8_BSLASH, kUTF8_SLASH);
540         toFile = fromFile.subString(fromDir.length());
541 
542         // Open the file to copy
543 
544         fstream inFile(fromFile.toCurrentCodePage()->buffer(),
545                     ios::in | STAF_ios_binary);
546 
547         if (!inFile)
548         {
549             updateResultString(outputList, fileEntry,
550                                kSTAFFileOpenError, osRC);
551             continue;
552         }
553 
554         // Get the size of the file (upperSize and lowerSize).
555         // If the file size is < 4G, upperSize will be zero.
556 
557         unsigned int upperSize = fileEntry->size().first;
558         unsigned int lowerSize = fileEntry->size().second;
559 
560         if (upperSize > 0)
561         {
562             // File size exceeds the maximum that the FS service handles
563 
564             updateResultString(outputList, fileEntry,
565                                kSTAFFileReadError, osRC);
566             // XXX: How provide a better error message such as the following?
567             //"File size exceeds maximum size ") + UINT_MAX + ") supported"
568 
569             inFile.close();
570             continue;
571         }
572 
573         unsigned int fileLength = lowerSize;
574 
575         // Determine if this file is to be text or binary
576 
577         textTransfer = false;
578         unsigned int isMatch;
579 
580         for(i = textExtList.begin(); i != textExtList.end(); i++)
581         {
582             isMatch = STAFFileSystem::matchesWildcards(
583                 fileEntry->path().extension(), *i, caseSensitive);
584             textTransfer |= (isMatch > 0);
585         }
586 
587         if (textTransfer)
588         {
589             // Determine what line ending the from file contains
590 
591             STAFServiceResult result = determineLineEnding(
592                 fileLength, inFile, fromFile, toMachine, currentEOL);
593 
594             if (result.fRC != kSTAFOk)
595             {
596                 updateResultString(outputList, fileEntry, result.fRC, osRC);
597                 // XXX: How provide a better error message such as
598                 // result.fResult?
599 
600                 continue;
601             }
602 
603             currentEOL = result.fResult;
604         }
605 
606         doCodepageConvert = doConvert && textTransfer;
607 
608         textTransfer &= (!newEOL.isEqualTo(currentEOL));
609 
610         connection->writeUInt(kSTAFFSContinueCopy);
611 
612         connection->writeUInt(kSTAFFSFile);
613         connection->writeString(toFile);
614 
615         if (level > 1)
616         {
617             if (doCodepageConvert)
618                 connection->writeUInt(kSTAFFSTextConvert);
619             else if (textTransfer)
620                 connection->writeUInt(kSTAFFSTextNoConvert);
621             else
622                 connection->writeUInt(kSTAFFSBinary);
623         }
624 
625         char fileBuffer[4000] = {0};
626         unsigned int writeLength = 0;
627 
628         STAFRC_t ack = static_cast<STAFRC_t>(connection->readUInt());
629         ackResult = connection->readString();
630 
631         if (ack != kSTAFOk)
632         {
633             updateResultString(outputList, fileEntry, ack, osRC);
634             inFile.close();
635             continue;
636         }
637 
638         if (doCodepageConvert)
639         {
640             // Perform a text transfer with conversion of eol chars and a
641             // codepage conversion.  It runs considerably slower than the
642             // binary transfer.  This is the type of transfer that will occur
643             // if the TEXTEXT option is specified and the codepages are
644             // different.  Data is sent as a series of buffers of UTF8 chars.
645 
646             gFSCopyManagerPtr->updateDirectoryCopy(
647                 copyDataPtr, fromFile, kSTAFFSTextConvert, fileLength);
648 
649             connection->writeString(currentEOL);
650 
651             if (fileLength > 0)
652             {
653                 STAFStringBufferPtr eolStr =
654                     currentEOL.toCurrentCodePage();
655 
656                 // How many bytes in the end-of-line sequence
657                 unsigned int eolBufferSize = eolStr->length();
658 
659                 // Points to a buffer containing end-of-line sequence
660                 char *eolBuffer = new char[eolBufferSize];
661 
662                 try
663                 {
664                     memcpy(eolBuffer, eolStr->buffer(), eolBufferSize);
665 
666                     // Last byte of end-of-line sequence
667                     char eolLastChar = eolBuffer[eolBufferSize - 1];
668 
669                     const unsigned int sBufferSize = 4096;
670 
671                     STAFRefPtr<char> buffer = STAFRefPtr<char>
672                         (new char[sBufferSize], STAFRefPtr<char>::INIT,
673                          STAFRefPtr<char>::ARRAY);
674 
675                     unsigned int bufferSize = sBufferSize;
676                     unsigned int readOffset = 0;  // Buffer read offset
677                     unsigned int bytesCopied  = 0;
678                     bool done = false;
679 
680                     while (!done)
681                     {
682                         rc = readFile(inFile,
683                                       static_cast<char *>(buffer + readOffset),
684                                       bufferSize - readOffset,
685                                       fromFile, toMachine,
686                                       fileLength, bytesCopied);
687 
688                         if (rc != kSTAFOk)
689                         {
690                             updateResultString(outputList, fileEntry,
691                                                rc, osRC);
692                             break;
693                         }
694 
695                         unsigned int bytesInBuffer = inFile.gcount() +
696                             readOffset;
697 
698                         bytesCopied += inFile.gcount();
699 
700                         if (bytesInBuffer < bufferSize) done = true;
701 
702                         // Find a newline. Make sure we don't underrun the
703                         // buffer.
704 
705                         unsigned int i = 0;
706                         unsigned int guardIndex = eolBufferSize - 1;
707 
708                         if (bytesInBuffer > 0)
709                         {
710                             i = bytesInBuffer - 1;  // Last NewLine index
711 
712                             while (((buffer[i] != eolLastChar) ||
713                                     !memcmp(buffer + i - eolBufferSize,
714                                             eolBuffer, eolBufferSize)) &&
715                                    (i > guardIndex))
716                             { --i; }
717                         }
718 
719                         while ((i == guardIndex) && !done)
720                         {
721                             // We have a line bigger than our buffer.
722                             // Note: the beginning of the buffer may be a lone
723                             // newline, but we ignore that for this algorithm
724                             // (as the next line is likely larger than the
725                             // buffer anyway.
726 
727                             // First, create a buffer that is double our
728                             // current size, and copy our existing buffer data
729                             // into it.
730 
731                             STAFRefPtr<char> tmpBuffer = STAFRefPtr<char>
732                                 (new char[bufferSize * 2],
733                                  STAFRefPtr<char>::INIT,
734                                  STAFRefPtr<char>::ARRAY);
735 
736                             memcpy(tmpBuffer, buffer, bufferSize);
737                             buffer = tmpBuffer;
738                             bufferSize *= 2;
739 
740                             // Now, read in data to fill remainder of the
741                             // buffer
742 
743                             rc = readFile(inFile, buffer + (bufferSize / 2),
744                                           bufferSize / 2, fromFile, toMachine,
745                                           fileLength, bytesCopied);
746 
747                             if (rc != kSTAFOk)
748                             {
749                                 updateResultString(outputList, fileEntry,
750                                                    rc, osRC);
751                                 break;
752                             }
753 
754                             bytesInBuffer += inFile.gcount();
755                             bytesCopied += inFile.gcount();
756 
757                             // Finally, let's check to make sure that this
758                             // buffer was big enough, by finding a newline.
759                             // Otherwise, let's run the loop again.
760 
761                             if (bytesInBuffer < bufferSize) done = true;
762 
763                             i = 0;
764 
765                             if (bytesInBuffer > 0)
766                             {
767                                 i = bytesInBuffer - 1;  // Last NewLine index
768                                 guardIndex = (bufferSize / 2) - eolBufferSize;
769 
770                                 while (((buffer[i] != eolLastChar) ||
771                                         !memcmp(buffer + i - eolBufferSize,
772                                                 eolBuffer, eolBufferSize)) &&
773                                        (i > guardIndex))
774                                 { --i; }
775                             }
776                         } // while ((i == guardIndex) && !done)
777 
778                         // We now have the last newline in the buffer
779 
780                         if (!done)
781                         {
782                             // Need to see if can create a STAFString (e.g.
783                             // without getting a codepage converter exception,
784                             // before writing kSTAFFSContinueCopy and the data
785 
786                             STAFString data = STAFString(
787                                 buffer, i + 1, STAFString::kCurrent);
788 
789                             connection->writeUInt(kSTAFFSContinueCopy);
790                             connection->writeString(data);
791 
792                             memmove(buffer, buffer + i + 1,
793                                     bufferSize - i - 1);
794 
795                             readOffset = bufferSize - i - 1;
796                         }
797                         else
798                         {
799                             // Need to see if can create a STAFString (e.g.
800                             // without getting a codepage converter exception,
801                             // before writing kSTAFFSContinueCopy and the data
802 
803                             STAFString data = STAFString(
804                                 buffer, bytesInBuffer, STAFString::kCurrent);
805 
806                             connection->writeUInt(kSTAFFSContinueCopy);
807                             connection->writeString(data);
808                         }
809 
810                         gFSCopyManagerPtr->updateFileCopy(copyDataPtr,
811                                                           bytesCopied);
812                     } // while (!done)
813                 }
814                 catch (STAFException &e)
815                 {
816                     // XXX: It would be nice if we could provide more
817                     //      information on the error using e.getText().
818 
819                     rc = e.getErrorCode();
820                     updateResultString(outputList, fileEntry, rc, osRC);
821                 }
822 
823                 delete[] eolBuffer;
824 
825             } // if (fileLength > 0)
826 
827             connection->writeUInt(kSTAFFSFinishedCopy);
828 
829             inFile.close();
830 
831             if (rc == kSTAFOk)
832             {
833                 // Read an ack, so that we know the file is closed
834 
835                 ack = connection->readUInt();
836 
837                 if (ack != kSTAFOk)
838                 {
839                     updateResultString(outputList, fileEntry, ack, osRC);
840                 }
841             }
842         }
843 
844         /*  This if clause is used to perform a text transfer with
845             conversion of eol chars without a codepage conversion
846             It runs considerably faster than the codepage conversion
847             transfer.
848             This is the type of transfer that will occur if the NOCONVERT
849             option is enabled
850             This is the type of transfer that will occur if the codepages
851             are the same and a text transfer has been specified
852         */
853         else if (textTransfer)
854         {
855             gFSCopyManagerPtr->updateDirectoryCopy(
856                 copyDataPtr, fromFile, kSTAFFSTextNoConvert, fileLength);
857 
858             STAFString transferString;
859             int bufferSize = 3000;
860             unsigned int transferLen = 0;
861             char *buffer = new char[bufferSize];
862             unsigned int bytesCopied = 0;
863 
864             connection->writeString(currentEOL);
865             connection->writeUInt(bufferSize);
866 
867             while ((fileLength > 0) && (inFile.good()))
868             {
869                 transferLen = STAF_MIN(bufferSize, fileLength);
870 
871                 rc = readFile(inFile, buffer, transferLen, fromFile,
872                               toMachine, fileLength, bytesCopied);
873 
874                 if (rc != kSTAFOk)
875                 {
876                     updateResultString(outputList, fileEntry, rc, osRC);
877                     fileLength = 0;
878                     break;
879                 }
880 
881                 connection->writeUInt(transferLen);
882                 connection->write(buffer, transferLen);
883 
884                 fileLength -= transferLen;
885                 bytesCopied += transferLen;
886                 gFSCopyManagerPtr->updateFileCopy(copyDataPtr, bytesCopied);
887             }
888 
889             connection->writeUInt(kSTAFFSFinishedCopy);
890 
891             delete[] buffer;
892             inFile.close();
893 
894             if (rc == kSTAFOk)
895             {
896                 // Read an ack, so that we know the file is closed
897 
898                 ack = connection->readUInt();
899 
900                 if (ack != kSTAFOk)
901                 {
902                     updateResultString(outputList, fileEntry, ack, osRC);
903                     fileLength = 0;
904                 }
905             }
906         }
907         else
908         {
909             // Perform a binary transfer of a file
910 
911             unsigned int bytesCopied = 0;
912 
913             gFSCopyManagerPtr->updateDirectoryCopy(
914                 copyDataPtr, fromFile, kSTAFFSBinary, fileLength);
915 
916             connection->writeUInt(fileLength);
917 
918             if (level > 3)
919             {
920                 // Starting with level 4 for the STAFDirectoryCopyAPI, to
921                 // improve performance, acks are no longer sent/received
922                 // after each read/write. Instead, a final ack is received
923                 // after the entire file is processed which indicates if
924                 // the copy was successful.
925 
926                 while ((fileLength > 0) && (inFile.good()))
927                 {
928                     writeLength = STAF_MIN(sizeof(fileBuffer), fileLength);
929 
930                     rc = readFile(
931                         inFile, reinterpret_cast<char *>(fileBuffer),
932                         writeLength, fromFile, toMachine, fileLength,
933                         bytesCopied);
934 
935                     if (rc != kSTAFOk) break;
936 
937                     connection->write(fileBuffer, writeLength);
938                     fileLength -= writeLength;
939                     bytesCopied += writeLength;
940                     gFSCopyManagerPtr->updateFileCopy(
941                         copyDataPtr, bytesCopied);
942                 }
943 
944                 inFile.close();
945 
946                 if (rc == kSTAFOk)
947                 {
948                     // Receive the final acknowledgement that indicates if
949                     // the file was copied successfully
950                     rc = connection->readUInt();
951                 }
952 
953                 if (rc != kSTAFOk)
954                 {
955                     // Update error information in result
956                     updateResultString(outputList, fileEntry, rc, osRC);
957                     fileLength = 0;
958                 }
959             }
960             else
961             {
962                 // Levels < 4 for the STAFDirectoryCopyAPI send/receive
963                 // acknowledgements after each read/write.
964 
965                 while ((fileLength > 0) && (inFile.good()))
966                 {
967                     writeLength = STAF_MIN(sizeof(fileBuffer), fileLength);
968 
969                     rc = readFile(
970                         inFile, reinterpret_cast<char *>(fileBuffer),
971                         writeLength, fromFile, toMachine, fileLength,
972                         bytesCopied);
973 
974                     if (rc != kSTAFOk)
975                     {
976                         updateResultString(outputList, fileEntry, rc, osRC);
977                         fileLength = 0;
978                         connection->writeUInt(kSTAFFileReadError);
979                         break;
980                     }
981 
982                     connection->writeUInt(kSTAFOk);
983                     connection->write(fileBuffer, writeLength);
984                     fileLength -= writeLength;
985 
986                     ack = connection->readUInt();
987 
988                     if (ack != kSTAFOk)
989                     {
990                         updateResultString(outputList, fileEntry, ack, osRC);
991                         fileLength = 0;
992                         break;
993                     }
994 
995                     bytesCopied += writeLength;
996                     gFSCopyManagerPtr->updateFileCopy(
997                         copyDataPtr, bytesCopied);
998                 }
999 
1000                 inFile.close();
1001             }
1002         }
1003     }
1004 
1005     return kSTAFOk;
1006 }
1007 
1008 
recurseCopyDir(STAFFSEntryPtr entry,const STAFString & namePattern,const STAFString & extPattern,STAFString fromDir,bool keepEmptyDir,bool onlyDir,unsigned int entryTypesUInt,STAFConnectionPtr connection,STAFFSCaseSensitive_t caseSensitive,SET_STAFString textExtList,STAFString currentEOL,STAFString newEOL,int level,bool doCodepageConvert,STAFObjectPtr & outputList,STAFFSCopyManager::FSCopyDataPtr copyDataPtr,const STAFString & toMachine)1009 STAFRC_t recurseCopyDir(STAFFSEntryPtr entry, const STAFString &namePattern,
1010     const STAFString &extPattern, STAFString fromDir, bool keepEmptyDir,
1011     bool onlyDir, unsigned int entryTypesUInt, STAFConnectionPtr connection,
1012     STAFFSCaseSensitive_t caseSensitive, SET_STAFString textExtList ,
1013     STAFString currentEOL, STAFString newEOL, int level, bool doCodepageConvert,
1014     STAFObjectPtr &outputList, STAFFSCopyManager::FSCopyDataPtr copyDataPtr,
1015     const STAFString &toMachine)
1016 {
1017     unsigned int osRC = 0;
1018     STAFRC_t rc = kSTAFOk;
1019 
1020     // Enumerate the files in the current entry
1021     STAFFSEnumPtr fileEnum = entry->enumerate(namePattern, extPattern,
1022         STAFFSEntryType_t(entryTypesUInt & ~kSTAFFSDirectory),
1023         kSTAFFSNoSort, caseSensitive);
1024 
1025     STAFString thisDir = entry->path().asString();
1026 
1027     // relative toDirectory
1028     STAFString toDir = thisDir.subString(fromDir.length());
1029     toDir = toDir.replace(kUTF8_BSLASH, kUTF8_SLASH);
1030 
1031     // If the thisDir is equal to fromDir, then the directory already exists
1032     // If there is at least one file to be copied or KEEPEMPTYDIRECTORIES
1033     // or ONLYDIRECTORIES option was used, then create the current directory.
1034     if ((thisDir != fromDir) &&
1035         (onlyDir || keepEmptyDir || (fileEnum->isValid())))
1036     {
1037         connection->writeUInt(kSTAFFSContinueCopy);
1038         connection->writeUInt(kSTAFFSDirectory);
1039         connection->writeString(toDir);
1040 
1041         //...wait for STAFProc's ack
1042         STAFRC_t ack = static_cast<STAFRC_t>(connection->readUInt());
1043         STAFString ackResult = connection->readString();
1044 
1045         if (ack != kSTAFOk)
1046             updateResultString(outputList, entry, ack, osRC);
1047     }
1048 
1049     // copy current directory
1050     if (!onlyDir && fileEnum->isValid())
1051         copyDirectory(fileEnum, fromDir, connection, textExtList, currentEOL,
1052                       newEOL, doCodepageConvert, level, caseSensitive,
1053                       outputList, copyDataPtr, toMachine);
1054 
1055     // Enumerate the directories in the current entry
1056     STAFFSEnumPtr directoryEnum = entry->enumerate(kUTF8_STAR, kUTF8_STAR,
1057         STAFFSEntryType_t(entryTypesUInt & ~kSTAFFSFile),
1058         kSTAFFSNoSort, caseSensitive);
1059 
1060     // copy sub-directories in current directory
1061     for (; directoryEnum->isValid(); directoryEnum->next())
1062     {
1063         recurseCopyDir(directoryEnum->entry(), namePattern, extPattern,
1064                        fromDir, keepEmptyDir, onlyDir, entryTypesUInt,
1065                        connection, caseSensitive, textExtList, currentEOL,
1066                        newEOL, level, doCodepageConvert, outputList,
1067                        copyDataPtr, toMachine);
1068     }
1069 
1070     return kSTAFOk;
1071 }
1072 
1073 
removeChildren(STAFFSEntryPtr entry,const STAFString & namePattern,const STAFString & extPattern,unsigned int entryTypesUInt,STAFFSCaseSensitive_t caseSensitive,STAFObjectPtr & outputList)1074 STAFRC_t removeChildren(STAFFSEntryPtr entry, const STAFString &namePattern,
1075                         const STAFString &extPattern,
1076                         unsigned int entryTypesUInt,
1077                         STAFFSCaseSensitive_t caseSensitive, STAFObjectPtr &outputList)
1078 {
1079     STAFFSEnumPtr childEnum = entry->enumerate(namePattern, extPattern,
1080                                                STAFFSEntryType_t(entryTypesUInt),
1081                                                kSTAFFSNoSort, caseSensitive);
1082     unsigned int osRC = 0;
1083     STAFRC_t rc = kSTAFOk;
1084 
1085     for (; childEnum->isValid(); childEnum->next())
1086     {
1087         STAFFSEntryPtr entry = childEnum->entry();
1088         rc = entry->remove(&osRC);
1089         updateResultString(outputList, entry, rc, osRC);
1090     }
1091 
1092     return kSTAFOk;
1093 }
1094 
1095 
recurseRemove(STAFFSEntryPtr entry,const STAFString & namePattern,const STAFString & extPattern,unsigned int entryTypesUInt,STAFFSCaseSensitive_t caseSensitive,STAFObjectPtr & outputList)1096 STAFRC_t recurseRemove(STAFFSEntryPtr entry, const STAFString &namePattern,
1097                        const STAFString &extPattern, unsigned int entryTypesUInt,
1098                        STAFFSCaseSensitive_t caseSensitive, STAFObjectPtr &outputList)
1099 {
1100     STAFFSEnumPtr childDirEnum = entry->enumerate(kUTF8_STAR, kUTF8_STAR,
1101                                                   kSTAFFSDirectory);
1102     STAFRC_t rc = kSTAFOk;
1103 
1104     for (; childDirEnum->isValid(); childDirEnum->next())
1105     {
1106         STAFFSEntryPtr childDirEntry = childDirEnum->entry();
1107 
1108         // If child directory entry is a link, skip it
1109 
1110         if (!childDirEntry->isLink())
1111         {
1112             rc = recurseRemove(childDirEntry, namePattern, extPattern,
1113                                entryTypesUInt, caseSensitive, outputList);
1114         }
1115     }
1116 
1117     rc = removeChildren(entry, namePattern, extPattern, entryTypesUInt,
1118                         caseSensitive, outputList);
1119 
1120     return kSTAFOk;
1121 }
1122 
1123 
addListDirectoryEntry(STAFString rootDir,STAFFSEntryPtr entry,STAFObjectPtr & outputList,STAFObjectPtr & mc,bool showLong,bool details)1124 STAFRC_t addListDirectoryEntry(
1125     STAFString rootDir, STAFFSEntryPtr entry, STAFObjectPtr &outputList,
1126     STAFObjectPtr &mc, bool showLong, bool details)
1127 {
1128     // Remove the root directory from the name
1129 
1130     STAFString theName = entry->path().asString();
1131     theName = theName.subString(rootDir.length() + 1);
1132 
1133     // Adds an entry in the specified format to the outputList.
1134     // Used when listing the contents of a directory..
1135 
1136     if (showLong && details)
1137     {
1138         STAFObjectPtr fileInfoMap = fListDetailsInfoClass->createInstance();
1139         fileInfoMap->put("name", theName);
1140         fileInfoMap->put("type", getTypeString(entry->type()));
1141         fileInfoMap->put("size", STAFString(entry->size64()));
1142         fileInfoMap->put("upperSize", STAFString(entry->size().first));
1143         fileInfoMap->put("lowerSize", STAFString(entry->size().second));
1144         fileInfoMap->put("lastModifiedTimestamp",
1145                          entry->modTime().asString());
1146 
1147         if (entry->linkTarget() != "")
1148             fileInfoMap->put("linkTarget", entry->linkTarget());
1149 
1150         outputList->append(fileInfoMap);
1151     }
1152     else if (showLong && !details)
1153     {
1154         STAFObjectPtr fileInfoMap =  fListLongInfoClass->createInstance();
1155         fileInfoMap->put("type", getTypeString(entry->type()));
1156 
1157         STAFString sizeInfo;
1158         STAFUInt64_t size = entry->size64();
1159 
1160         if (size > (99999 * 1024))
1161             sizeInfo = STAFString(size / (1024 * 1024)) + "M"; // Megabytes
1162         else if (size > 99999)
1163             sizeInfo = STAFString(size / 1024) + "K"; // Kilobytes
1164         else
1165             sizeInfo = STAFString(size);  // Bytes
1166 
1167         fileInfoMap->put("size", sizeInfo);
1168 
1169         fileInfoMap->put("lastModifiedTimestamp", entry->modTime().asString());
1170         fileInfoMap->put("name", theName);
1171 
1172         if (entry->linkTarget() != "")
1173             fileInfoMap->put("linkTarget", entry->linkTarget());
1174 
1175         outputList->append(fileInfoMap);
1176     }
1177     else
1178     {
1179         outputList->append(theName);
1180     }
1181 
1182     return kSTAFOk;
1183 }
1184 
1185 
1186 /*****************************************************************************/
1187 /* getDirectorySize - Gets the total size of the entries in a directory      */
1188 /*                    whose name, extension, type, and case-sensitivity      */
1189 /*                    match the specified criteria.  If the recurse argument */
1190 /*                    is true, the size will also include the sizes of the   */
1191 /*                    entries in subdirectories that match the specified     */
1192 /*                    criteria.                                              */
1193 /*                                                                           */
1194 /* Accepts: (In)  File system entry (must be for a directory)                */
1195 /*          (In)  Name pattern (* indicates to match the name of any entry   */
1196 /*                in the directory)                                          */
1197 /*          (In)  Extension pattern (* indicates to match the extension of   */
1198 /*                any entry in the directory)                                */
1199 /*          (In)  Types (types of entries in the directory that match)       */
1200 /*          (In)  Case sensitive (indicates whether to match the name and    */
1201 /*                extension patterns in a case sensitive manner)             */
1202 /*          (In)  Recurse (true indicates to count matching entries in       */
1203 /*                subdirectories)                                            */
1204 /*          (Out) Total size of the directory                                */
1205 /*          (Out) Number of files in the directory                           */
1206 /*          (Out) Number of subdirectories in the directory                  */
1207 /*                                                                           */
1208 /* Returns: Standard return codes                                            */
1209 /*****************************************************************************/
getDirectorySize(const STAFFSEntryPtr dirEntry,const STAFString & namePattern,const STAFString & extPattern,const unsigned int entryTypesUInt,const STAFFSCaseSensitive_t caseSensitive,const bool recurse,STAFUInt64_t & size,STAFUInt64_t & numFiles,STAFUInt64_t & numDirectories)1210 STAFRC_t getDirectorySize(const STAFFSEntryPtr dirEntry,
1211                           const STAFString &namePattern,
1212                           const STAFString &extPattern,
1213                           const unsigned int entryTypesUInt,
1214                           const STAFFSCaseSensitive_t caseSensitive,
1215                           const bool recurse,
1216                           STAFUInt64_t &size, STAFUInt64_t &numFiles,
1217                           STAFUInt64_t &numDirectories)
1218 {
1219     // Enumerate all entries in the directory with the specified types,
1220     // and also all sub-directories if the recurse option was specified
1221 
1222     STAFFSEntryType_t entryTypes;
1223 
1224     if (recurse)
1225         entryTypes = STAFFSEntryType_t(entryTypesUInt | kSTAFFSDirectory);
1226     else
1227         entryTypes = STAFFSEntryType_t(entryTypesUInt);
1228 
1229     // Get an enumeration of the entries (specify no sorting since only
1230     // getting a summary)
1231 
1232     STAFFSEnumPtr dirEnum = dirEntry->enumerate(
1233         kUTF8_STAR, kUTF8_STAR, entryTypes, kSTAFFSNoSort, caseSensitive);
1234 
1235     // Iterate through the entries
1236 
1237     for (; dirEnum->isValid(); dirEnum->next())
1238     {
1239         STAFFSEntryPtr entry = dirEnum->entry();
1240 
1241         // Check if name and extension (in the specified case) and type
1242         // match the specified criteria
1243 
1244         if ((STAFFileSystem::matchesWildcards(entry->path().name(),
1245                                               namePattern, caseSensitive)) &&
1246             (STAFFileSystem::matchesWildcards(entry->path().extension(),
1247                                               extPattern, caseSensitive)) &&
1248             (entry->type() & STAFFSEntryType_t(entryTypesUInt)))
1249         {
1250             // This entry matches the specified criteria
1251 
1252             if (!entry->isLink())
1253             {
1254                 // If the entry is a link, the size is for the entry it is
1255                 // linked to (which may not even be in the same directory)
1256                 // so don't include the size of a link entry
1257 
1258                 size += entry->size64();
1259             }
1260 
1261             if (entry->type() & kSTAFFSDirectory)
1262                 numDirectories++;
1263             else
1264                 numFiles++;
1265         }
1266 
1267         // If recurse was specified and the entry is a directory, get the total
1268         // size of the directory
1269 
1270         if (recurse && (entry->type() & kSTAFFSDirectory))
1271         {
1272             // Skip special directories . and ..
1273 
1274             if ((entry->path().name() == sPeriod) ||
1275                 (entry->path().name() == sDoublePeriod))
1276                continue;
1277 
1278             getDirectorySize(
1279                 entry, namePattern, extPattern, entryTypesUInt, caseSensitive,
1280                 recurse, size, numFiles, numDirectories);
1281         }
1282     }
1283 
1284     return kSTAFOk;
1285 }
1286 
1287 
recurseListDir(STAFFSEntryPtr dirEntry,const STAFString & namePattern,const STAFString & extPattern,unsigned int entryTypesUInt,STAFFSCaseSensitive_t caseSensitive,STAFFSSortBy_t sortBy,STAFFSEntryList & entryList)1288 STAFRC_t recurseListDir(
1289     STAFFSEntryPtr dirEntry, const STAFString &namePattern,
1290     const STAFString &extPattern, unsigned int entryTypesUInt,
1291     STAFFSCaseSensitive_t caseSensitive, STAFFSSortBy_t sortBy,
1292     STAFFSEntryList &entryList)
1293 {
1294     // Enumerate all entries whose type matches one specified, as well as
1295     // enumerate all directory entries
1296 
1297     STAFFSEnumPtr dirEnum = dirEntry->enumerate(
1298         kUTF8_STAR, kUTF8_STAR,
1299         STAFFSEntryType_t(STAFFSEntryType_t(entryTypesUInt) |
1300                           kSTAFFSDirectory),
1301         sortBy, caseSensitive);
1302 
1303     // Iterate through all the entriess
1304 
1305     for (; dirEnum->isValid(); dirEnum->next())
1306     {
1307         STAFFSEntryPtr entry = dirEnum->entry();
1308 
1309         // Check if name and extension (in the specifies case) and type
1310         // match the specified criteria
1311 
1312         if ((STAFFileSystem::matchesWildcards(entry->path().name(),
1313                                               namePattern, caseSensitive)) &&
1314             (STAFFileSystem::matchesWildcards(entry->path().extension(),
1315                                               extPattern, caseSensitive)) &&
1316             (entry->type() & STAFFSEntryType_t(entryTypesUInt)))
1317         {
1318             // This entries matches the specified criteria so add an entry
1319             // to the matching entry list
1320 
1321             entryList.push_back(entry);
1322         }
1323 
1324         // Check if entry is a directory, recursively check if any of it's
1325         // entries match the specified criteria
1326 
1327         if (entry->type() & kSTAFFSDirectory)
1328         {
1329             // Skip special directories . and ..
1330 
1331             if ((entry->path().name() == sPeriod) ||
1332                 (entry->path().name() == sDoublePeriod))
1333                continue;
1334 
1335             recurseListDir(entry, namePattern, extPattern, entryTypesUInt,
1336                            caseSensitive, sortBy, entryList);
1337         }
1338     }
1339 
1340     return kSTAFOk;
1341 }
1342 
1343 
convertToHex(const char * buffer,unsigned int fileLength)1344 STAFServiceResult convertToHex(const char *buffer,
1345                                unsigned int fileLength)
1346 {
1347     // Convert the buffer to a hex format
1348 
1349     STAFBuffer<char> hexBuffer(new char[fileLength * 2],
1350                                STAFBuffer<char>::INIT,
1351                                STAFBuffer<char>::ARRAY);
1352 
1353     for (unsigned int i = 0, j = 0; i < fileLength; i++)
1354     {
1355         hexBuffer[j++] = HEX_TABLE[(buffer[i] >> 4) & 0xF];
1356         hexBuffer[j++] = HEX_TABLE[buffer[i] & 0xF];
1357     }
1358 
1359     // Return new result in hex format
1360     return STAFServiceResult(kSTAFOk, STAFString(hexBuffer, fileLength * 2,
1361                                                  STAFString::kUTF8));
1362 }
1363 
1364 
convertLineEndings(const char * buffer,unsigned int fileLength,const STAFString & textFormat,const STAFString & orgMachine,bool isLocalRequest,bool testFlag)1365 STAFServiceResult convertLineEndings(const char *buffer,
1366                                      unsigned int fileLength,
1367                                      const STAFString &textFormat,
1368                                      const STAFString &orgMachine,
1369                                      bool isLocalRequest,
1370                                      bool testFlag)
1371 {
1372     // Convert the line ending characters in the buffer
1373 
1374     // Determine the new line ending character(s) to use
1375 
1376     STAFString newEOL = STAFString("");
1377 
1378     if (textFormat.toUpperCase() == "NATIVE")
1379     {
1380         if (!isLocalRequest)
1381         {
1382             // Try to get the line separator for the originating machine.
1383             // If VAR resolve request fails (probably due to insufficient
1384             // trust), default to target system's line separator.
1385 
1386             // XXX: In STAF V3.x, change to get the line separator from
1387             //      information passed to the service.
1388 
1389             STAFResultPtr lineSepResult = gSTAFProcHandlePtr->submit(
1390                 orgMachine, "VAR", "RESOLVE STRING " +
1391                 STAFHandle::wrapData("{STAF/Config/Sep/Line}"));
1392 
1393             if (lineSepResult->rc == kSTAFOk)
1394             {
1395                 // XXX: If the originating machine is running STAF v2.x,
1396                 //      using the new VAR resolve string syntax does not
1397                 //      return a 7 due to the parser assuming the rest of
1398                 //      the string should be the value, so it line separator
1399                 //      begins with "STRING " (which is NOT what we want).
1400                 if (lineSepResult->result.find("STRING ") != 0)
1401                     newEOL = lineSepResult->result;
1402                 else
1403                 {
1404                     // Try using the old VAR resolve syntax in case the
1405                     // originating machine is running STAF v2.x.
1406                     lineSepResult = gSTAFProcHandlePtr->submit(
1407                         orgMachine, "VAR", "RESOLVE " +
1408                         STAFHandle::wrapData("{STAF/Config/Sep/Line}"));
1409 
1410                     if (lineSepResult->rc == kSTAFOk)
1411                         newEOL = lineSepResult->result;
1412                 }
1413             }
1414         }
1415 
1416         if (newEOL == STAFString(""))
1417         {
1418             // Get the line ending character(s) for the system where the
1419             // file resides.
1420 
1421             STAFConfigInfo configInfo;
1422             STAFString_t errorBufferT;
1423             unsigned int osRC = 0;
1424 
1425             if (STAFUtilGetConfigInfo(&configInfo, &errorBufferT, &osRC)
1426                 != kSTAFOk)
1427             {
1428                  return STAFServiceResult(
1429                      kSTAFBaseOSError,
1430                      STAFString(errorBufferT, STAFString::kShallow) +
1431                      ", RC: " + osRC);
1432             }
1433 
1434             // Default to the target system's line separator if target
1435             // system is the same as the orginating system (e.g. is local) or
1436             // if can't get the originating system's line separator.
1437 
1438             newEOL = configInfo.lineSeparator;
1439         }
1440     }
1441     else if (textFormat.toUpperCase() == "WINDOWS")
1442         newEOL = sWindowsEOL;
1443     else if (textFormat.toUpperCase() == "UNIX")
1444         newEOL = sUnixEOL;
1445     else
1446         newEOL = textFormat;
1447 
1448     // Create a STAFString containing the file data
1449 
1450     STAFString result = "";
1451 
1452     try
1453     {
1454         result = STAFString(buffer, fileLength);
1455     }
1456     catch (STAFException &e)
1457     {
1458         result = e.getText();
1459 
1460         if (e.getErrorCode() == kSTAFConverterError)
1461         {
1462             result += ": The file contains data that is not valid in the "
1463                 "codepage that STAF is using.  To see the codepage that STAF "
1464                 "is using, check the value of STAF variable "
1465                 "STAF/Config/CodePage.";
1466         }
1467 
1468         return STAFServiceResult(e.getErrorCode(), result);
1469     }
1470     catch (...)
1471     {
1472         result = "Caught unknown exception in STAFFSService::"
1473             "convertLineEndings() when trying to create a STAFString from "
1474             "the data in the file";
1475         return STAFServiceResult(kSTAFUnknownError, result);
1476     }
1477 
1478     // Check which line endings the file actually contains and assign
1479     // to currentEOL.  The first line-ending found for Windows (0D0A)
1480     // or Unix (0A) determines the line endings assumed for the file.
1481     // If no line-endings are found in the file, default to newEOL
1482     // so that no line-endings conversion is done.
1483 
1484     STAFString currentEOL = newEOL;
1485 
1486     unsigned int windowsEOLIndex = result.find(sWindowsEOL);
1487     unsigned int unixEOLIndex = result.find(sUnixEOL);
1488 
1489     if (windowsEOLIndex < unixEOLIndex)
1490         currentEOL = sWindowsEOL;
1491     else if (unixEOLIndex < windowsEOLIndex)
1492         currentEOL = sUnixEOL;
1493 
1494     // If debug flag is true, prints line ending characters in hex
1495 
1496     bool debug = false;
1497 
1498     if (debug)
1499     {
1500         cout << "currentEOL in Hex: ";
1501 
1502         const char *buffer = currentEOL.buffer();
1503 
1504         for (unsigned int y = 0; y < currentEOL.length(); ++y)
1505         {
1506             unsigned int currChar = static_cast<unsigned char>(buffer[y]);
1507             if (currChar < 16) cout << "0";
1508             cout << hex << currChar << dec << " ";
1509         }
1510 
1511         cout << endl << "newEOL in Hex:  ";
1512 
1513         buffer = newEOL.buffer();
1514 
1515         for (unsigned int i = 0; i < newEOL.length(); ++i)
1516         {
1517             unsigned int currChar = static_cast<unsigned char>(buffer[i]);
1518             if (currChar < 16) cout << "0";
1519             cout << hex << currChar << dec << " ";
1520         }
1521 
1522         cout << endl << endl;
1523     }
1524 
1525     // If conversion of line ending character(s) is needed, replace the
1526     // current line ending character(s) with the new line ending character(s)
1527 
1528     if (currentEOL != newEOL)
1529         result = result.replace(currentEOL, newEOL);
1530 
1531     // If testFlag, convert the result to hex to verify that the line-ending
1532     // characters were converted as expected.
1533 
1534     if (testFlag)
1535         return convertToHex(result.buffer(), result.length());
1536 
1537     // Return new result in hex format
1538     return STAFServiceResult(kSTAFOk, result);
1539 }
1540 
1541 
STAFFSService()1542 STAFFSService::STAFFSService() : STAFService("FS")
1543 {
1544     // Assign the help text string for the service
1545 
1546     sHelpMsg = STAFString("*** FS Service Help ***") +
1547         *gLineSeparatorPtr + *gLineSeparatorPtr +
1548         "COPY   FILE <Name> [TOFILE <Name> | TODIRECTORY <Name>] "
1549         "[TOMACHINE <Machine>]" +
1550         *gLineSeparatorPtr +
1551         "       [TEXT [FORMAT <Format>]] [FAILIFEXISTS | FAILIFNEW]" +
1552         //Remove comment when enabling convert/noconvert option
1553         //"       [CONVERT | NOCONVERT]]" +
1554         *gLineSeparatorPtr + *gLineSeparatorPtr +
1555         "COPY   DIRECTORY <Name> [TODIRECTORY <Name>] [TOMACHINE <Machine>]" +
1556         *gLineSeparatorPtr +
1557         "       [NAME <Pattern>] [EXT <Pattern>] "
1558         "[CASESENSITIVE | CASEINSENSITIVE]" +
1559         *gLineSeparatorPtr +
1560         "       [TEXTEXT <Pattern>... [FORMAT <Format>]] " +
1561         // Remove comment when enabling convert/noconvert option
1562         //"[CONVERT | NOCONVERT]]" +
1563         *gLineSeparatorPtr +
1564         "       [RECURSE [KEEPEMPTYDIRECTORIES | ONLYDIRECTORIES]]" +
1565         *gLineSeparatorPtr +
1566         "       [IGNOREERRORS] [FAILIFEXISTS | FAILIFNEW]" +
1567         *gLineSeparatorPtr + *gLineSeparatorPtr +
1568         "MOVE   FILE <Name> <TOFILE <Name> | TODIRECTORY <Name>>" +
1569         *gLineSeparatorPtr + *gLineSeparatorPtr +
1570         "MOVE   DIRECTORY <Name> TODIRECTORY <Name>" +
1571         *gLineSeparatorPtr + *gLineSeparatorPtr +
1572         "GET    FILE <Name> [[TEXT | BINARY] [FORMAT <Format>]]" +
1573         *gLineSeparatorPtr + *gLineSeparatorPtr +
1574         "GET    ENTRY <Name> <TYPE | SIZE | MODTIME | LINKTARGET" +
1575     #ifdef STAF_USE_SSL
1576         " | " +
1577         *gLineSeparatorPtr +
1578         "                     CHECKSUM [<Algorithm>]" +
1579     #endif
1580         ">" +
1581         *gLineSeparatorPtr +
1582         "QUERY  ENTRY <Name>" +
1583         *gLineSeparatorPtr + *gLineSeparatorPtr +
1584         "CREATE DIRECTORY <Name> [FULLPATH] [FAILIFEXISTS]" +
1585         *gLineSeparatorPtr + *gLineSeparatorPtr +
1586         "LIST   DIRECTORY <Name> [RECURSE] [LONG [DETAILS] | SUMMARY] "
1587         "[TYPE <Types>]" +
1588         *gLineSeparatorPtr +
1589         "       [NAME <Pattern>] [EXT <Pattern>] "
1590         "[CASESENSITIVE | CASEINSENSITIVE]" +
1591         *gLineSeparatorPtr +
1592         "       [SORTBYNAME | SORTBYSIZE | SORTBYMODTIME]" +
1593         *gLineSeparatorPtr + *gLineSeparatorPtr +
1594         "LIST   COPYREQUESTS [LONG] [INBOUND] [OUTBOUND]" +
1595         *gLineSeparatorPtr +
1596         "       [FILE [[BINARY] [TEXT]]] [DIRECTORY]" +
1597         *gLineSeparatorPtr + *gLineSeparatorPtr +
1598         "LIST   SETTINGS" + *gLineSeparatorPtr + *gLineSeparatorPtr +
1599         "DELETE ENTRY <Name> CONFIRM [RECURSE] [IGNOREERRORS]" +
1600         *gLineSeparatorPtr +
1601         "       [ CHILDREN [TYPE <Types>] [NAME <Pattern>] [EXT <Pattern>]" +
1602         *gLineSeparatorPtr +
1603         "                  [CASESENSITIVE | CASEINSENSITIVE] ]" +
1604         *gLineSeparatorPtr + *gLineSeparatorPtr +
1605         "SET    STRICTFSCOPYTRUST <Enabled | Disabled>" +
1606         *gLineSeparatorPtr + *gLineSeparatorPtr +
1607         "HELP";
1608 
1609     // Create the command request parsers
1610 
1611     // COPY FILE parser setup
1612 
1613     fCopyFileParser.addOption("COPY",                 1,
1614         STAFCommandParser::kValueNotAllowed);
1615     fCopyFileParser.addOption("FILE",                 1,
1616         STAFCommandParser::kValueRequired);
1617     fCopyFileParser.addOption("TOFILE",               1,
1618         STAFCommandParser::kValueRequired);
1619     fCopyFileParser.addOption("TODIRECTORY",          1,
1620         STAFCommandParser::kValueRequired);
1621     fCopyFileParser.addOption("TOMACHINE",            1,
1622         STAFCommandParser::kValueRequired);
1623     fCopyFileParser.addOption("FORMAT",               1,
1624         STAFCommandParser::kValueRequired);
1625     fCopyFileParser.addOption("TEXT",                 1,
1626         STAFCommandParser::kValueNotAllowed);
1627     // Remove comment when enabling convert/noconvert option
1628     /*fCopyFileParser.addOption("CONVERT",              1,
1629         STAFCommandParser::kValueNotAllowed);
1630     fCopyFileParser.addOption("NOCONVERT",            1,
1631         STAFCommandParser::kValueNotAllowed);*/
1632     fCopyFileParser.addOption("FAILIFEXISTS",         1,
1633         STAFCommandParser::kValueNotAllowed);
1634     fCopyFileParser.addOption("FAILIFNEW",            1,
1635         STAFCommandParser::kValueNotAllowed);
1636 
1637     fCopyFileParser.addOptionGroup("FAILIFEXISTS FAILIFNEW", 0, 1);
1638     fCopyFileParser.addOptionGroup("TOFILE TODIRECTORY", 0, 1);
1639 
1640     // Remove comment when enabling convert/noconvert option
1641     //fCopyFileParser.addOptionGroup("CONVERT NOCONVERT", 0, 1);
1642 
1643     fCopyFileParser.addOptionNeed("FORMAT", "TEXT");
1644 
1645     // Remove comment when enabling convert/noconvert option
1646     //fCopyFileParser.addOptionNeed("CONVERT", "TEXT");
1647     //fCopyFileParser.addOptionNeed("NOCONVERT", "TEXT");
1648     fCopyFileParser.addOptionNeed("COPY", "FILE");
1649 
1650     // COPY DIRECTORY parser setup
1651 
1652     fCopyDirParser.addOption("COPY",                 1,
1653         STAFCommandParser::kValueNotAllowed);
1654     fCopyDirParser.addOption("DIRECTORY",            1,
1655         STAFCommandParser::kValueRequired);
1656     fCopyDirParser.addOption("TODIRECTORY",          1,
1657         STAFCommandParser::kValueRequired);
1658     fCopyDirParser.addOption("TOMACHINE",            1,
1659         STAFCommandParser::kValueRequired);
1660     fCopyDirParser.addOption("NAME",                 1,
1661         STAFCommandParser::kValueRequired);
1662     fCopyDirParser.addOption("EXT",                  1,
1663         STAFCommandParser::kValueRequired);
1664     fCopyDirParser.addOption("TEXTEXT",              0,
1665         STAFCommandParser::kValueRequired);
1666     fCopyDirParser.addOption("FORMAT",               1,
1667         STAFCommandParser::kValueRequired);
1668     // Remove comment when enabling convert/noconvert option
1669     /*fCopyDirParser.addOption("CONVERT",              1,
1670         STAFCommandParser::kValueNotAllowed);
1671     fCopyDirParser.addOption("NOCONVERT",            1,
1672         STAFCommandParser::kValueNotAllowed);*/
1673     fCopyDirParser.addOption("RECURSE",              1,
1674         STAFCommandParser::kValueNotAllowed);
1675     fCopyDirParser.addOption("CASESENSITIVE",        1,
1676         STAFCommandParser::kValueNotAllowed);
1677     fCopyDirParser.addOption("CASEINSENSITIVE",      1,
1678         STAFCommandParser::kValueNotAllowed);
1679     fCopyDirParser.addOption("KEEPEMPTYDIRECTORIES", 1,
1680         STAFCommandParser::kValueNotAllowed);
1681     fCopyDirParser.addOption("ONLYDIRECTORIES",      1,
1682         STAFCommandParser::kValueNotAllowed);
1683     fCopyDirParser.addOption("IGNOREERRORS",         1,
1684         STAFCommandParser::kValueNotAllowed);
1685     fCopyDirParser.addOption("FAILIFEXISTS",         1,
1686         STAFCommandParser::kValueNotAllowed);
1687     fCopyDirParser.addOption("FAILIFNEW",            1,
1688         STAFCommandParser::kValueNotAllowed);
1689 
1690     fCopyDirParser.addOptionGroup("CASESENSITIVE CASEINSENSITIVE", 0, 1);
1691     fCopyDirParser.addOptionGroup("KEEPEMPTYDIRECTORIES ONLYDIRECTORIES", 0, 1);
1692     fCopyDirParser.addOptionGroup("FAILIFEXISTS FAILIFNEW", 0, 1);
1693     // Remove comment when enabling convert/noconvert option
1694     //fCopyDirParser.addOptionGroup("CONVERT NOCONVERT", 0, 1);
1695 
1696     fCopyDirParser.addOptionNeed("FORMAT", "TEXTEXT");
1697     // Remove comment when enabling convert/noconvert option
1698     //fCopyDirParser.addOptionNeed("CONVERT", "TEXTEXT");
1699     //fCopyDirParser.addOptionNeed("NOCONVERT", "TEXTEXT");
1700     fCopyDirParser.addOptionNeed("KEEPEMPTYDIRECTORIES ONLYDIRECTORIES",
1701                               "RECURSE");
1702     fCopyDirParser.addOptionNeed("COPY", "DIRECTORY");
1703 
1704     // MOVE FILE parser setup
1705 
1706     fMoveFileParser.addOption("MOVE", 1, STAFCommandParser::kValueNotAllowed);
1707     fMoveFileParser.addOption("FILE", 1, STAFCommandParser::kValueRequired);
1708     fMoveFileParser.addOption("TOFILE", 1, STAFCommandParser::kValueRequired);
1709     fMoveFileParser.addOption("TODIRECTORY", 1, STAFCommandParser::kValueRequired);
1710     fMoveFileParser.addOptionGroup("TOFILE TODIRECTORY", 1, 1);
1711     fMoveFileParser.addOptionNeed("MOVE", "FILE");
1712 
1713     // MOVE DIRECTORY parser setup
1714 
1715     fMoveDirParser.addOption("MOVE", 1, STAFCommandParser::kValueNotAllowed);
1716     fMoveDirParser.addOption(
1717         "DIRECTORY", 1, STAFCommandParser::kValueRequired);
1718     fMoveDirParser.addOption(
1719         "TODIRECTORY", 1, STAFCommandParser::kValueRequired);
1720     fMoveDirParser.addOptionNeed("MOVE", "DIRECTORY");
1721     fMoveDirParser.addOptionNeed("DIRECTORY", "TODIRECTORY");
1722 
1723     // GET parser setup
1724 
1725     fGetParser.addOption("GET",              1,
1726         STAFCommandParser::kValueNotAllowed);
1727     fGetParser.addOption("FILE",             1,
1728         STAFCommandParser::kValueRequired);
1729 
1730     fGetParser.addOption("TEXT",             1,
1731         STAFCommandParser::kValueNotAllowed);
1732     fGetParser.addOption("BINARY",           1,
1733         STAFCommandParser::kValueNotAllowed);
1734     fGetParser.addOption("FORMAT",           1,
1735         STAFCommandParser::kValueRequired);
1736 
1737     // Note that TEST is an undocumented option that we can use for testing
1738     // that the line ending character really got converted correctly
1739     fGetParser.addOption("TEST",           1,
1740      STAFCommandParser::kValueNotAllowed);
1741 
1742     fGetParser.addOption("ENTRY",            1,
1743         STAFCommandParser::kValueRequired);
1744     fGetParser.addOption("TYPE",             1,
1745         STAFCommandParser::kValueNotAllowed);
1746     fGetParser.addOption("SIZE",             1,
1747         STAFCommandParser::kValueNotAllowed);
1748     fGetParser.addOption("MODTIME",          1,
1749         STAFCommandParser::kValueNotAllowed);
1750     fGetParser.addOption("LINKTARGET",       1,
1751         STAFCommandParser::kValueNotAllowed);
1752 #ifdef STAF_USE_SSL
1753     fGetParser.addOption("CHECKSUM",         1,
1754         STAFCommandParser::kValueAllowed);
1755 #endif
1756 
1757     fGetParser.addOptionGroup("FILE ENTRY", 1, 1);
1758 
1759     fGetParser.addOptionGroup("TEXT BINARY", 0, 1);
1760     fGetParser.addOptionNeed("TEXT BINARY", "FILE");
1761     fGetParser.addOptionNeed("FORMAT", "TEXT BINARY");
1762     fGetParser.addOptionNeed("TEST", "FILE");
1763 
1764     fGetParser.addOptionNeed("GET", "FILE ENTRY");
1765 #ifdef STAF_USE_SSL
1766     fGetParser.addOptionNeed("TYPE SIZE MODTIME LINKTARGET CHECKSUM", "ENTRY");
1767     fGetParser.addOptionNeed("ENTRY", "TYPE SIZE MODTIME LINKTARGET CHECKSUM");
1768 #else
1769     fGetParser.addOptionNeed("TYPE SIZE MODTIME LINKTARGET", "ENTRY");
1770     fGetParser.addOptionNeed("ENTRY", "TYPE SIZE MODTIME LINKTARGET");
1771 #endif
1772 
1773     // LIST parser setup
1774 
1775     fListParser.addOption(
1776         "LIST", 1, STAFCommandParser::kValueNotAllowed);
1777     fListParser.addOption(
1778         "DIRECTORY", 1, STAFCommandParser::kValueRequired);
1779     fListParser.addOption(
1780         "NAME", 1, STAFCommandParser::kValueRequired);
1781     fListParser.addOption(
1782         "EXT", 1, STAFCommandParser::kValueRequired);
1783     fListParser.addOption(
1784         "LONG", 1, STAFCommandParser::kValueNotAllowed);
1785     fListParser.addOption(
1786         "DETAILS", 1, STAFCommandParser::kValueNotAllowed);
1787     fListParser.addOption(
1788         "SUMMARY", 1, STAFCommandParser::kValueNotAllowed);
1789     fListParser.addOption(
1790         "SORTBYNAME", 1, STAFCommandParser::kValueNotAllowed);
1791     fListParser.addOption(
1792         "SORTBYSIZE", 1, STAFCommandParser::kValueNotAllowed);
1793     fListParser.addOption(
1794         "SORTBYMODTIME", 1, STAFCommandParser::kValueNotAllowed);
1795     fListParser.addOption(
1796         "CASESENSITIVE", 1, STAFCommandParser::kValueNotAllowed);
1797     fListParser.addOption(
1798         "CASEINSENSITIVE", 1, STAFCommandParser::kValueNotAllowed);
1799     fListParser.addOption(
1800         "TYPE", 1, STAFCommandParser::kValueRequired);
1801     fListParser.addOption(
1802         "RECURSE", 1, STAFCommandParser::kValueNotAllowed);
1803     fListParser.addOption(
1804         "SETTINGS", 1, STAFCommandParser::kValueNotAllowed);
1805 
1806     fListParser.addOptionGroup("DIRECTORY SETTINGS", 1, 1);
1807     fListParser.addOptionGroup("SORTBYNAME SORTBYSIZE SORTBYTIME", 0, 1);
1808     fListParser.addOptionGroup("CASESENSITIVE CASEINSENSITIVE", 0, 1);
1809     fListParser.addOptionGroup("LONG SUMMARY", 0, 1);
1810 
1811     fListParser.addOptionNeed("LIST", "DIRECTORY SETTINGS");
1812     fListParser.addOptionNeed(
1813         "NAME EXT LONG SUMMARY SORTBYNAME SORTBYSIZE SORTBYMODTIME TYPE RECURSE",
1814         "DIRECTORY");
1815     fListParser.addOptionNeed("DETAILS", "LONG");
1816     fListParser.addOptionNeed("CASESENSITIVE CASEINSENSITIVE",
1817                               "NAME EXT SORTBYNAME");
1818 
1819     // LIST COPYREQUESTS parser setup
1820 
1821     fListCopyRequestsParser.addOption(
1822         "LIST", 1, STAFCommandParser::kValueNotAllowed);
1823     fListCopyRequestsParser.addOption(
1824         "COPYREQUESTS", 1, STAFCommandParser::kValueNotAllowed);
1825     fListCopyRequestsParser.addOption(
1826         "INBOUND", 1, STAFCommandParser::kValueNotAllowed);
1827     fListCopyRequestsParser.addOption(
1828         "OUTBOUND", 1, STAFCommandParser::kValueNotAllowed);
1829     fListCopyRequestsParser.addOption(
1830         "FILE", 1, STAFCommandParser::kValueNotAllowed);
1831     fListCopyRequestsParser.addOption(
1832         "DIRECTORY", 1, STAFCommandParser::kValueNotAllowed);
1833     fListCopyRequestsParser.addOption(
1834         "BINARY", 1, STAFCommandParser::kValueNotAllowed);
1835     fListCopyRequestsParser.addOption(
1836         "TEXT", 1, STAFCommandParser::kValueNotAllowed);
1837     fListCopyRequestsParser.addOption(
1838         "LONG", 1, STAFCommandParser::kValueNotAllowed);
1839 
1840     fListCopyRequestsParser.addOptionNeed("BINARY TEXT", "FILE");
1841 
1842     // CREATE parser setup
1843 
1844     fCreateParser.addOption("CREATE",           1,
1845         STAFCommandParser::kValueNotAllowed);
1846     fCreateParser.addOption("DIRECTORY",        1,
1847         STAFCommandParser::kValueRequired);
1848     fCreateParser.addOption("FULLPATH",         1,
1849         STAFCommandParser::kValueNotAllowed);
1850     fCreateParser.addOption("FAILIFEXISTS",     1,
1851         STAFCommandParser::kValueNotAllowed);
1852 
1853     fCreateParser.addOptionNeed("CREATE", "DIRECTORY");
1854 
1855     // DELETE parser setup
1856 
1857     fDeleteParser.addOption("DELETE",           1,
1858         STAFCommandParser::kValueNotAllowed);
1859     fDeleteParser.addOption("ENTRY",            1,
1860         STAFCommandParser::kValueRequired);
1861     fDeleteParser.addOption("CHILDREN",         1,
1862         STAFCommandParser::kValueNotAllowed);
1863     fDeleteParser.addOption("NAME",             1,
1864         STAFCommandParser::kValueRequired);
1865     fDeleteParser.addOption("EXT",              1,
1866         STAFCommandParser::kValueRequired);
1867     fDeleteParser.addOption("TYPE",             1,
1868         STAFCommandParser::kValueRequired);
1869     fDeleteParser.addOption("CASESENSITIVE",    1,
1870         STAFCommandParser::kValueNotAllowed);
1871     fDeleteParser.addOption("CASEINSENSITIVE",  1,
1872         STAFCommandParser::kValueNotAllowed);
1873     fDeleteParser.addOption("RECURSE",          1,
1874         STAFCommandParser::kValueNotAllowed);
1875     fDeleteParser.addOption("CONFIRM",          1,
1876         STAFCommandParser::kValueNotAllowed);
1877     fDeleteParser.addOption("IGNOREERRORS",     1,
1878         STAFCommandParser::kValueNotAllowed);
1879 
1880     fDeleteParser.addOptionGroup("CASESENSITIVE CASEINSENSITIVE", 0, 1);
1881 
1882     fDeleteParser.addOptionNeed("DELETE", "ENTRY");
1883     fDeleteParser.addOptionNeed("DELETE", "CONFIRM");
1884     fDeleteParser.addOptionNeed("NAME EXT TYPE", "CHILDREN");
1885     fDeleteParser.addOptionNeed("CASESENSITIVE CASEINSENSITIVE", "NAME EXT");
1886     fDeleteParser.addOptionNeed("IGNOREERRORS", "CHILDREN RECURSE");
1887 
1888     // QUERY parser setup
1889 
1890     fQueryParser.addOption("QUERY",            1,
1891         STAFCommandParser::kValueNotAllowed);
1892     fQueryParser.addOption("ENTRY",            1,
1893         STAFCommandParser::kValueRequired);
1894 
1895     fQueryParser.addOptionNeed("QUERY", "ENTRY");
1896 
1897     // SET parser setup
1898 
1899     fSetParser.addOption("SET", 1,
1900                          STAFCommandParser::kValueNotAllowed);
1901     fSetParser.addOption("STRICTFSCOPYTRUST", 1,
1902                          STAFCommandParser::kValueRequired);
1903 
1904     // set groups/needs
1905 
1906     fSetParser.addOptionGroup("STRICTFSCOPYTRUST", 1, 1);
1907 
1908     // Construct map-class for a LIST DIRECTORY LONG information
1909 
1910     fListLongInfoClass = STAFMapClassDefinition::create(
1911         "STAF/Service/FS/ListLongInfo");
1912 
1913     fListLongInfoClass->addKey("type", "Type");
1914     fListLongInfoClass->addKey("size", "Size");
1915     fListLongInfoClass->addKey("lastModifiedTimestamp",
1916                                "Modified Date-Time");
1917     fListLongInfoClass->addKey("name", "Name");
1918     fListLongInfoClass->addKey("linkTarget", "Link Target");
1919     fListLongInfoClass->setKeyProperty(
1920         "linkTarget", "display-short-name", "Link");
1921 
1922     // Construct map-class for LIST DIRECTORY LONG DETAILS information
1923 
1924     fListDetailsInfoClass = STAFMapClassDefinition::create(
1925         "STAF/Service/FS/ListDetailsInfo");
1926 
1927     fListDetailsInfoClass->addKey("name", "Name");
1928     fListDetailsInfoClass->addKey("linkTarget", "Link Target");
1929     fListDetailsInfoClass->setKeyProperty(
1930         "linkTarget", "display-short-name", "Link");
1931     fListDetailsInfoClass->addKey("type", "Type");
1932     fListDetailsInfoClass->addKey("size", "Size");
1933     fListDetailsInfoClass->addKey("upperSize", "U-Size");
1934     fListDetailsInfoClass->addKey("lowerSize", "L-Size");
1935     fListDetailsInfoClass->addKey("lastModifiedTimestamp",
1936                                   "Modified Date-Time");
1937 
1938     // Construct map-class for a LIST DIRECTORY SUMMARY information
1939 
1940     fListSummaryInfoClass = STAFMapClassDefinition::create(
1941         "STAF/Service/FS/ListSummaryInfo");
1942 
1943     fListSummaryInfoClass->addKey("name", "Name");
1944     fListSummaryInfoClass->addKey("size", "Size");
1945     fListSummaryInfoClass->addKey("numFiles", "Files");
1946     fListSummaryInfoClass->addKey("numDirectories", "Directories");
1947 
1948     // Construct map class for LIST COPYREQUESTS information
1949 
1950     fCopyRequestClass = STAFMapClassDefinition::create(
1951         "STAF/Service/FS/CopyRequest");
1952 
1953     fCopyRequestClass->addKey("startTimestamp", "Start Date-Time");
1954     fCopyRequestClass->setKeyProperty(
1955         "timestamp", "display-short-name", "Date-Time");
1956     fCopyRequestClass->addKey("io", "In/Out");
1957     fCopyRequestClass->setKeyProperty(
1958         "io", "display-short-name", "I/O");
1959     fCopyRequestClass->addKey("machine", "Machine");
1960     fCopyRequestClass->addKey("name", "Name");
1961     fCopyRequestClass->addKey("type", "Type");
1962 
1963     // Construct map class for COPYREQUESTS LONG information for a file copy
1964 
1965     fCopyFileClass = STAFMapClassDefinition::create(
1966         "STAF/Service/FS/CopyFile");
1967 
1968     fCopyFileClass->addKey("startTimestamp", "Start Date-Time");
1969     fCopyFileClass->setKeyProperty(
1970         "timestamp", "display-short-name", "Date-Time");
1971     fCopyFileClass->addKey("io", "In/Out");
1972     fCopyFileClass->setKeyProperty(
1973         "io", "display-short-name", "I/O");
1974     fCopyFileClass->addKey("machine", "Machine");
1975     fCopyFileClass->addKey("name", "File Name");
1976     fCopyFileClass->addKey("type", "Type");
1977     fCopyFileClass->addKey("mode", "Mode");
1978     fCopyFileClass->addKey("state", "Transfer State");
1979 
1980     // Construct map class for the state of a file copy request
1981 
1982     fFileCopyStateClass = STAFMapClassDefinition::create(
1983         "STAF/Service/FS/FileCopyState");
1984     fFileCopyStateClass->addKey("fileSize", "File Size");
1985     fFileCopyStateClass->addKey("bytesCopied", "Bytes Copied");
1986 
1987     // Construct map class for COPYREQUESTS LONG information for a directory
1988 
1989     fCopyDirectoryClass = STAFMapClassDefinition::create(
1990         "STAF/Service/FS/CopyDirectory");
1991 
1992     fCopyDirectoryClass->addKey("startTimestamp", "Start Date-Time");
1993     fCopyDirectoryClass->setKeyProperty(
1994         "timestamp", "display-short-name", "Date-Time");
1995     fCopyDirectoryClass->addKey("io", "In/Out");
1996     fCopyDirectoryClass->setKeyProperty(
1997         "io", "display-short-name", "I/O");
1998     fCopyDirectoryClass->addKey("machine", "Machine");
1999     fCopyDirectoryClass->addKey("name", "Directory Name");
2000     fCopyDirectoryClass->addKey("type", "Type");
2001     fCopyDirectoryClass->addKey("state", "Transfer State");
2002 
2003     // Construct map class for the state of a directory copy request
2004 
2005     fDirectoryCopyStateClass = STAFMapClassDefinition::create(
2006         "STAF/Service/FS/DirectoryCopyState");
2007     fDirectoryCopyStateClass->addKey("name", "Name");
2008     fDirectoryCopyStateClass->addKey("mode", "Mode");
2009     fDirectoryCopyStateClass->addKey("fileSize", "File Size");
2010     fDirectoryCopyStateClass->addKey("bytesCopied", "Bytes Copied");
2011 
2012     // Construct map class for LIST SETTINGS information
2013 
2014     fSettingsClass = STAFMapClassDefinition::create(
2015         "STAF/Service/FS/Settings");
2016 
2017     fSettingsClass->addKey("strictFSCopyTrust", "Strict FS Copy Trust");
2018 
2019     // Construct map-class for GET ENTRY SIZE information
2020 
2021     fEntrySizeInfoClass = STAFMapClassDefinition::create(
2022         "STAF/Service/FS/SizeInfo");
2023 
2024     fEntrySizeInfoClass->addKey("size", "Size");
2025     fEntrySizeInfoClass->addKey("upperSize", "Upper 32-bit Size");
2026     fEntrySizeInfoClass->addKey("lowerSize", "Lower 32-bit Size");
2027 
2028     // Construct map-class for QUERY ENTRY information
2029 
2030     fQueryInfoClass = STAFMapClassDefinition::create(
2031         "STAF/Service/FS/QueryInfo");
2032 
2033     fQueryInfoClass->addKey("name", "Name");
2034     fQueryInfoClass->addKey("linkTarget", "Link Target");
2035     fQueryInfoClass->addKey("type", "Type");
2036     fQueryInfoClass->addKey("size", "Size");
2037     fQueryInfoClass->addKey("upperSize", "Upper 32-bit Size");
2038     fQueryInfoClass->addKey("lowerSize", "Lower 32-bit Size");
2039     fQueryInfoClass->addKey("lastModifiedTimestamp", "Modified Date-Time");
2040 
2041     // Construct map-class for error information on a copy/delete
2042 
2043     fErrorInfoClass = STAFMapClassDefinition::create(
2044         "STAF/Service/FS/ErrorInfo");
2045 
2046     fErrorInfoClass->addKey("name", "Name");
2047     fErrorInfoClass->addKey("rc", "RC");
2048     fErrorInfoClass->addKey("osRC", "OS RC");
2049 
2050 #ifdef STAF_USE_SSL
2051     // Perform SSL Thread Setup and assign callback functions needed for
2052     // multi-threaded applications that use OpenSSL
2053     // Note:  Need to do this because the FS service uses OpenSSL functions
2054     //        for determining the checksum of files
2055 
2056     STAF_SSL_Thread_Setup();
2057 #endif
2058 }
2059 
2060 
~STAFFSService()2061 STAFFSService::~STAFFSService()
2062 {
2063 #ifdef STAF_USE_SSL
2064     // Free SSL Thread callbacks and mutexes used by the locking
2065     // callback function
2066     // Note:  Need to do this because the FS service uses OpenSSL functions
2067     //        for determining the checksum of files
2068 
2069     STAF_SSL_Thread_Cleanup();
2070 #endif
2071 }
2072 
2073 
info(unsigned int) const2074 STAFString STAFFSService::info(unsigned int) const
2075 {
2076     return name() + ": Internal";
2077 }
2078 
2079 
acceptRequest(const STAFServiceRequest & requestInfo)2080 STAFServiceResult STAFFSService::acceptRequest(
2081     const STAFServiceRequest &requestInfo)
2082 {
2083     STAFString action = requestInfo.fRequest.subWord(0, 1).toLowerCase();
2084     STAFString subAction = requestInfo.fRequest.subWord(1, 1).toLowerCase();
2085 
2086     if (action == "copy")
2087     {
2088         if (subAction == "file")
2089             return handleCopyFile(requestInfo);
2090         else if (subAction == "directory")
2091             return handleCopyDir(requestInfo);
2092         else
2093         {
2094             STAFString errMsg = STAFString("'") +
2095                 requestInfo.fRequest.subWord(0, 1) + " " +
2096                 requestInfo.fRequest.subWord(1, 1) +
2097                 "' is not a valid command request for the " + name() +
2098                 " service" + *gLineSeparatorPtr + *gLineSeparatorPtr +
2099                 sHelpMsg;
2100 
2101             return STAFServiceResult(kSTAFInvalidRequestString, errMsg);
2102         }
2103     }
2104     else if (action == "move")
2105     {
2106         if (subAction == "file")
2107             return handleMove(true, requestInfo);
2108         else if (subAction == "directory")
2109             return handleMove(false, requestInfo);
2110         else
2111         {
2112             STAFString errMsg = STAFString("'") +
2113                 requestInfo.fRequest.subWord(0, 1) + " " +
2114                 requestInfo.fRequest.subWord(1, 1) +
2115                 "' is not a valid command request for the " + name() +
2116                 " service" + *gLineSeparatorPtr + *gLineSeparatorPtr +
2117                 sHelpMsg;
2118 
2119             return STAFServiceResult(kSTAFInvalidRequestString, errMsg);
2120         }
2121     }
2122     else if (action == "get")    return handleGet(requestInfo);
2123     else if (action == "list")
2124     {
2125         if (subAction == "copyrequests")
2126             return handleListCopyRequests(requestInfo);
2127         else
2128             return handleList(requestInfo);
2129     }
2130     else if (action == "create") return handleCreate(requestInfo);
2131     else if (action == "delete") return handleDelete(requestInfo);
2132     else if (action == "query")  return handleQuery(requestInfo);
2133     else if (action == "set")    return handleSet(requestInfo);
2134     else if (action == "help")   return handleHelp(requestInfo);
2135     else
2136     {
2137         STAFString errMsg = STAFString("'") +
2138             requestInfo.fRequest.subWord(0, 1) +
2139             "' is not a valid command request for the " + name() +
2140             " service" + *gLineSeparatorPtr + *gLineSeparatorPtr +
2141             sHelpMsg;
2142 
2143         return STAFServiceResult(kSTAFInvalidRequestString, errMsg);
2144     }
2145 }
2146 
2147 
handleCopyFile(const STAFServiceRequest & requestInfo)2148 STAFServiceResult STAFFSService::handleCopyFile(
2149     const STAFServiceRequest &requestInfo)
2150 {
2151     // Verify that the requesting machine/user has at least trust level 4
2152 
2153     IVALIDATE_TRUST(4, "COPY");
2154 
2155     // Parse the request
2156 
2157     STAFCommandParseResultPtr parsedResult = fCopyFileParser.parse(
2158         requestInfo.fRequest);
2159 
2160     if (parsedResult->rc != kSTAFOk)
2161     {
2162         return STAFServiceResult(kSTAFInvalidRequestString,
2163                                  parsedResult->errorBuffer, 0);
2164     }
2165 
2166     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
2167     STAFString   fromMachine = requestInfo.fEndpoint;
2168     STAFHandle_t handle      = requestInfo.fHandle;
2169     STAFString   request     = requestInfo.fRequest;
2170     STAFRC_t     rc          = kSTAFOk;
2171     STAFString   toMachine   = fromMachine;
2172     STAFString   toFile;
2173     STAFString   fromFile;
2174     STAFString   errorBuffer;
2175 
2176     rc = RESOLVE_STRING_OPTION("FILE", fromFile);
2177 
2178     if (!rc) rc = RESOLVE_OPTIONAL_STRING_OPTION("TOMACHINE", toMachine);
2179 
2180     if (rc) return STAFServiceResult(rc, errorBuffer);
2181 
2182     // if user specified TOFILE value
2183     if (parsedResult->optionTimes("TOFILE") != 0)
2184     {
2185         toFile = parsedResult->optionValue("TOFILE");
2186     }
2187     else if (parsedResult->optionTimes("TODIRECTORY") != 0)
2188     {
2189         // Copy the file to this directory using the same file name as
2190         // the "from" file name
2191         toFile = parsedResult->optionValue("TODIRECTORY");
2192     }
2193     else
2194     {
2195         toFile = parsedResult->optionValue("FILE");
2196     }
2197 
2198     // Note:  We'll resolve any variables in TOFILE on the TOMACHINE not
2199     //        here on the from machine.
2200 
2201     // Check if the from file exists
2202 
2203     STAFFSPath fromPath(fromFile);
2204 
2205     try
2206     {
2207         if (!fromPath.exists())
2208             return STAFServiceResult(
2209                 kSTAFDoesNotExist, "File " + fromFile + " does not exist");
2210     }
2211     catch (STAFBaseOSErrorException &e)
2212     {
2213         STAFString errMsg = "Error on From File: " + fromFile + "\n" +
2214             e.getText() + STAFString(": ") + e.getErrorCode();
2215 
2216         return STAFServiceResult(kSTAFBaseOSError, errMsg);
2217     }
2218 
2219     // Get the actual path name and remove any unnecessary trailing slashes
2220     fromFile = fromPath.setRoot(fromPath.root()).asString();
2221 
2222     // If TODIRECTORY is specified, need to add the from file name to the path
2223     if (parsedResult->optionTimes("TODIRECTORY") != 0)
2224     {
2225         toFile = toFile + "/" + fromPath.name();
2226 
2227         if (fromPath.extension() != "")
2228             toFile = toFile + "." + fromPath.extension();
2229     }
2230 
2231     // determine if transfer is text or binary
2232     bool doText = false;
2233     bool doCodepage = false;
2234     STAFString newEOL = "Native";
2235 
2236     if (parsedResult->optionTimes("TEXT") > 0)
2237     {
2238         doText = true;
2239         doCodepage = true;
2240 
2241         rc = RESOLVE_OPTIONAL_STRING_OPTION("FORMAT", newEOL);
2242         if (rc) return STAFServiceResult(rc, errorBuffer);
2243 
2244         // If enable NOCONVERT option, uncomment the  following line
2245         //doCodepage = (parsedResult->optionTimes("NOCONVERT") == 0);
2246     }
2247 
2248     fstream inFile;
2249 
2250     // open the file as binary
2251     inFile.open(fromFile.toCurrentCodePage()->buffer(),
2252                  ios::in | STAF_ios_binary);
2253 
2254     if (!inFile)
2255         return STAFServiceResult(kSTAFFileOpenError, fromFile);
2256 
2257     // Get the size of the file (upperSize and lowerSize).
2258     // If the file size is < 4G, upperSize will be zero.
2259 
2260     STAFFSEntryPtr entry;
2261 
2262     try
2263     {
2264         entry = fromPath.getEntry();
2265     }
2266     catch (STAFBaseOSErrorException &e)
2267     {
2268         STAFString errMsg = "Error on From File: " + fromFile + "\n" +
2269             e.getText() + STAFString(": ") + e.getErrorCode();
2270 
2271         return STAFServiceResult(kSTAFBaseOSError, errMsg);
2272     }
2273 
2274     unsigned int upperSize = entry->size().first;
2275     unsigned int lowerSize = entry->size().second;
2276 
2277     // Check if file size exceeds the maximum that the FS service handles
2278 
2279     if (upperSize > 0)
2280     {
2281         STAFString errMsg = STAFString(
2282             "File size exceeds the maximum size (") + UINT_MAX +
2283             ") supported.  File name: " + fromFile;
2284         return STAFServiceResult(kSTAFFileReadError, errMsg);
2285     }
2286 
2287     unsigned int fileLength = lowerSize;
2288 
2289     unsigned char fileBuffer[4000] = { 0 };
2290     unsigned int writeLength = 0;
2291     STAFConnectionPtr connection;
2292     STAFConnectionProviderPtr provider;
2293     STAFString result;
2294     unsigned int useNewAPI = 1;
2295     unsigned int levelToUse = 0;
2296     unsigned int flags = 0;
2297 
2298     if (parsedResult->optionTimes("FAILIFEXISTS") != 0)
2299         flags |= 0x00000001;
2300     else if (parsedResult->optionTimes("FAILIFNEW") != 0)
2301         flags |= 0x00000002;
2302 
2303     // If interface cycling is not enabled, and toMachine is not 'local',
2304     // and neither interface nor port are specified in toMachine, get the
2305     // originator's interface and prepend to the toMachine value so that
2306     // the connection to the toMachine will use the orginator's interface.
2307 
2308     if ((!gConnectionManagerPtr->getAutoInterfaceCycling()) &&
2309         (!isLocalMachine(toMachine, 1)) &&
2310         (toMachine.find(gSpecSeparator == STAFString::kNPos)) &&
2311         (toMachine.find(kUTF8_AT) == STAFString::kNPos))
2312     {
2313         unsigned int specSepPos = requestInfo.fEndpoint.find(gSpecSeparator);
2314 
2315         if (specSepPos != STAFString::kNPos)
2316         {
2317             STAFString interface = requestInfo.fEndpoint.subString(
2318                 0, specSepPos);
2319 
2320             if (interface != "local")
2321             {
2322                 // Prepend the interface from the originator's endpoint
2323                 toMachine = interface + gSpecSeparator + toMachine;
2324             }
2325         }
2326     }
2327 
2328     // This flag indicates if copyDataPtr has been initialized yet by calling
2329     // gFSCopyManagerPtr->add() and passing it copyDataPtr
2330 
2331     bool addedFSCopyData = false;
2332 
2333     STAFFSCopyManager::FSCopyDataPtr copyDataPtr;
2334 
2335     try
2336     {
2337         // Make a connection to the toMachine
2338 
2339         try
2340         {
2341             rc = gConnectionManagerPtr->makeConnection(
2342                 toMachine, provider, connection, result);
2343         }
2344         catch (STAFConnectionProviderConnectException &e)
2345         {
2346             rc = kSTAFNoPathToMachine;
2347             result = e.getText() + STAFString(": ") +
2348                 STAFString(e.getErrorCode());
2349         }
2350 
2351         if ((rc == kSTAFNoPathToMachine) &&
2352             (toMachine.find(kUTF8_AT) == STAFString::kNPos))
2353         {
2354             // Retry connecting to the toMachine, this time using the
2355             // originator's port
2356 
2357             unsigned int atPos = requestInfo.fEndpoint.find(kUTF8_AT);
2358 
2359             if (atPos != STAFString::kNPos)
2360             {
2361                 toMachine = toMachine + requestInfo.fEndpoint.subString(atPos);
2362 
2363                 rc = gConnectionManagerPtr->makeConnection(
2364                     toMachine, provider, connection, result);
2365             }
2366         }
2367 
2368         if (rc) return STAFServiceResult(rc, result);
2369 
2370         // First, lets try the new API
2371 
2372         connection->writeUInt(kSTAFFileTransferAPI2);  // New API number
2373         connection->writeUInt(0);    // Dummy level
2374 
2375         STAFRC_t ack = connection->readUInt();
2376 
2377         if (ack != kSTAFOk)
2378         {
2379             // They don't support the new API, so try the old API
2380             useNewAPI = 0;
2381 
2382             rc = gConnectionManagerPtr->makeConnection(
2383                 toMachine, connection, result);
2384 
2385             if (rc) return STAFServiceResult(rc, result);
2386 
2387             connection->writeUInt(kSTAFFileTransferAPI);  // Old API Number
2388             connection->writeUInt(0);    // API Level
2389 
2390             ack = connection->readUInt();
2391 
2392             if (ack != kSTAFOk) return ack;
2393         }
2394         else
2395         {
2396             // Now find out the specific level to use
2397             unsigned int minLevel = 1;
2398             unsigned int maxLevel = 3;
2399 
2400             connection->writeUInt(minLevel);
2401             connection->writeUInt(maxLevel);
2402 
2403             levelToUse = connection->readUInt();
2404 
2405             if (levelToUse == 0) return kSTAFInvalidAPILevel;
2406         }
2407 
2408         // If the other machine can do a text transfer, specify text or binary
2409 
2410         if (useNewAPI && (levelToUse > 1))
2411         {
2412             if (doText)
2413             {
2414                 if (doCodepage)
2415                     connection->writeUInt(kSTAFFSTextConvert);
2416                 else
2417                     connection->writeUInt(kSTAFFSTextNoConvert);
2418             }
2419             else
2420             {
2421                 connection->writeUInt(kSTAFFSBinary);
2422             }
2423         }
2424 
2425         connection->writeString(*gMachinePtr);
2426         connection->writeString(requestInfo.fMachine);
2427         connection->writeString(toFile);
2428 
2429         if (!(useNewAPI && (levelToUse > 1)) && doText)
2430         {
2431             // Fail and abort
2432             flags |= (0x00000001 | 0x00000002);
2433 
2434             // Send bad flags 0x1|0x2 // fail if new | fail if exist
2435             // to kill other side
2436             connection->writeUInt(flags);
2437 
2438             // Read kill confirmation
2439             ack = static_cast<STAFRC_t>(connection->readUInt());
2440             result = connection->readString();
2441             inFile.close();
2442             return kSTAFInvalidAPILevel;
2443         }
2444 
2445         // Add an entry to the FS Copy Map so can list copies in progress
2446 
2447         int copyMode = kSTAFFSBinary;
2448 
2449         if (doCodepage)
2450             copyMode = kSTAFFSTextConvert;   // Text, cp conversion
2451         else if (doText)
2452             copyMode = kSTAFFSTextNoConvert; // Text, no cp conversion
2453 
2454         if (gFSCopyManagerPtr->add(fromFile, toMachine, kSTAFFSCopyFrom,
2455                                    kSTAFFSFileCopy, copyMode,
2456                                    fileLength, copyDataPtr))
2457         {
2458             return STAFServiceResult(
2459                 kSTAFFileReadError, "Cannot read from a file that another "
2460                 "copy request is currently writing to");
2461         }
2462 
2463         addedFSCopyData = true;
2464 
2465         // Write the flags
2466 
2467         connection->writeUInt(flags);
2468 
2469         if (useNewAPI && levelToUse > 2)
2470         {
2471             connection->writeUInt(requestInfo.fHandle);
2472 
2473             if (requiresSecureConnection(requestInfo.fAuthenticator) &&
2474                 (provider->getProperty(
2475                     kSTAFConnectionProviderIsSecureProperty) != "1"))
2476             {
2477                 // Don't send authentication data since non-secure
2478                 // connection.  Instead set authenticator to "none" and
2479                 // user to "anonymous" and set authentication data to ""
2480 
2481                 connection->writeString(gNoneString);
2482                 connection->writeString(gAnonymousString);
2483                 connection->writeString("");
2484             }
2485             else
2486             {
2487                 connection->writeString(requestInfo.fAuthenticator);
2488                 connection->writeString(requestInfo.fUserIdentifier);
2489                 connection->writeString(requestInfo.fAuthenticationData);
2490             }
2491 
2492             connection->writeString(requestInfo.fInterface);
2493             connection->writeString(requestInfo.fLogicalInterfaceID);
2494             connection->writeString(requestInfo.fPhysicalInterfaceID);
2495             connection->writeString(requestInfo.fSTAFInstanceUUID);
2496             connection->writeString(*gSTAFInstanceUUIDPtr);
2497 
2498             // Write request var pool
2499 
2500             STAFVariablePool::VariableMap requestVarMap =
2501                 requestInfo.fRequestVarPool->getVariableMapCopy();
2502 
2503             connection->writeUInt(requestVarMap.size());
2504 
2505             for (STAFVariablePool::VariableMap::iterator requestIter =
2506                  requestVarMap.begin();
2507                  requestIter != requestVarMap.end();
2508                  ++requestIter)
2509             {
2510                 connection->writeString(requestIter->second.name);
2511                 connection->writeString(requestIter->second.value);
2512             }
2513 
2514             // Write source shared var pool
2515 
2516             STAFVariablePool::VariableMap sourceSharedVarMap =
2517                 requestInfo.fSourceSharedVarPool->getVariableMapCopy();
2518 
2519             connection->writeUInt(sourceSharedVarMap.size());
2520 
2521             for (STAFVariablePool::VariableMap::iterator sourceSharedIter =
2522                      sourceSharedVarMap.begin();
2523                  sourceSharedIter != sourceSharedVarMap.end();
2524                  ++sourceSharedIter)
2525             {
2526                 connection->writeString(sourceSharedIter->second.name);
2527                 connection->writeString(sourceSharedIter->second.value);
2528             }
2529         }
2530 
2531         // Check if can open and write to the file
2532 
2533         ack = static_cast<STAFRC_t>(connection->readUInt());
2534         result = connection->readString();
2535 
2536         if (ack != kSTAFOk)
2537         {
2538             gFSCopyManagerPtr->remove(copyDataPtr);
2539 
2540             return STAFServiceResult(ack, result);
2541         }
2542 
2543         STAFString currentEOL = STAFString("");
2544 
2545         if (doText)
2546         {
2547             // Determine what line ending the from file contains
2548 
2549             STAFServiceResult result = determineLineEnding(
2550                 fileLength, inFile, fromFile, toMachine, currentEOL);
2551 
2552             if (result.fRC != kSTAFOk)
2553             {
2554                 gFSCopyManagerPtr->remove(copyDataPtr);
2555 
2556                 return result;
2557             }
2558 
2559             currentEOL = result.fResult;
2560 
2561             // send the new EOL marker across
2562             connection->writeString(newEOL);
2563 
2564             // verify ok new eol
2565             if (connection->readUInt() == kSTAFFSStopCopy)
2566             {
2567                 gFSCopyManagerPtr->remove(copyDataPtr);
2568 
2569                 return STAFServiceResult(kSTAFBaseOSError,
2570                                         "Get current EOL failure");
2571             }
2572 
2573             newEOL = connection->readString();
2574 
2575             if (newEOL.isEqualTo(currentEOL))
2576             {
2577                 // Allows changing to use binary transfer (which is faster) if
2578                 // the eol on both sides are the same and no convert is needed.
2579 
2580                 connection->writeUInt(kSTAFFSBinary);
2581                 doText = false;
2582             }
2583             else
2584                 connection->writeUInt(kSTAFFSTextNoConvert);
2585         }
2586 
2587         if (doCodepage)
2588         {
2589             // Check if the codepages on both sides are the same because then
2590             // can use the text noconvert routine which is faster.
2591 
2592             // Get this codepage id
2593             STAFString cPage;
2594             rc = RESOLVE_STRING("{STAF/Config/CodePage}", cPage);
2595 
2596             // Get other codepage id
2597             STAFString otherCPage = connection->readString();
2598 
2599             if (cPage.isEqualTo(otherCPage))
2600             {
2601                 // XXX: Change next line to kSTAFFSTextNoConvert if decide not
2602                 //      to force codepage conversion on all text transfers
2603                 connection->writeUInt(kSTAFFSTextConvert);
2604 
2605                 // XXX: Uncomment next line to enable the codepage filter.
2606                 //      Currently, commented out to force codepage conversion
2607                 //      on all text transfers.
2608                 //doCodepage = false;
2609             }
2610             else
2611                 connection->writeUInt(kSTAFFSTextConvert);
2612         }
2613 
2614         /************** Start transferring the file ***************/
2615 
2616         if (doCodepage)
2617         {
2618             // Perform a text transfer with conversion of eol chars and a
2619             // codepage conversion.  It runs considerably slower than the
2620             // binary transfer.  This is the type of transfer that will
2621             // occur if the TEXT option is specified and the codepages are
2622             // different.  Data is sent as a series of buffers of UTF8 chars.
2623 
2624             gFSCopyManagerPtr->updateFileCopy1(
2625                 copyDataPtr, fileLength, kSTAFFSTextConvert);
2626 
2627             unsigned int bytesCopied = 0;
2628 
2629             connection->writeString(currentEOL);
2630 
2631             if (fileLength > 0)
2632             {
2633                 STAFStringBufferPtr eolStr = currentEOL.toCurrentCodePage();
2634 
2635                 // How many bytes in the end-of-line sequence
2636                 unsigned int eolBufferSize = eolStr->length();
2637 
2638                 // Points to a buffer containing end-of-line sequence
2639                 char *eolBuffer = new char[eolBufferSize];
2640                 memcpy(eolBuffer, eolStr->buffer(), eolBufferSize);
2641 
2642                 // Last byte of end-of-line sequence
2643                 char eolLastChar = eolBuffer[eolBufferSize - 1];
2644 
2645                 const unsigned int sBufferSize = 4096;
2646 
2647                 STAFRefPtr<char> buffer = STAFRefPtr<char>
2648                     (new char[sBufferSize], STAFRefPtr<char>::INIT,
2649                      STAFRefPtr<char>::ARRAY);
2650                 unsigned int bufferSize = sBufferSize;
2651                 unsigned int readOffset = 0;  // Buffer read offset
2652                 bool done = false;
2653 
2654                 while (!done)
2655                 {
2656                     rc = readFile(
2657                         inFile, static_cast<char *>(buffer + readOffset),
2658                         bufferSize - readOffset, fromFile,
2659                         toMachine, fileLength, bytesCopied);
2660 
2661                     if (rc != kSTAFOk)
2662                     {
2663                         result = "Unrecoverable read error occurred while"
2664                                  " copying file " + fromFile + " in text mode";
2665                         break;
2666                     }
2667 
2668                     unsigned int bytesInBuffer = inFile.gcount() + readOffset;
2669 
2670                     bytesCopied += inFile.gcount();
2671 
2672                     if (bytesInBuffer < bufferSize) done = true;
2673 
2674                     // Find a newline.  Make sure we don't underrun the buffer
2675 
2676                     unsigned int i = 0;
2677                     unsigned int guardIndex = eolBufferSize - 1;
2678 
2679                     if (bytesInBuffer > 0)
2680                     {
2681                         i = bytesInBuffer - 1;  // Last NewLine index
2682 
2683                         while (((buffer[i] != eolLastChar) ||
2684                                 !memcmp(buffer + i - eolBufferSize,
2685                                         eolBuffer, eolBufferSize)) &&
2686                                (i > guardIndex))
2687                         { --i; }
2688                     }
2689 
2690                     while ((i == guardIndex) && !done)
2691                     {
2692                         // We have a line bigger than our buffer.
2693                         // Note: the beginning of the buffer may be a lone
2694                         // newline, but we ignore that for this algorithm
2695                         // (as the next line is likely larger than the buffer
2696                         // anyway.
2697 
2698                         // First, create a buffer that is double our current
2699                         // size, and copy our existing buffer data into it
2700 
2701                         STAFRefPtr<char> tmpBuffer = STAFRefPtr<char>
2702                             (new char[bufferSize * 2], STAFRefPtr<char>::INIT,
2703                              STAFRefPtr<char>::ARRAY);
2704 
2705                         memcpy(tmpBuffer, buffer, bufferSize);
2706                         buffer = tmpBuffer;
2707                         bufferSize *= 2;
2708 
2709                         // Now, read in data to fill remainder of the buffer
2710 
2711                         rc = readFile(inFile, buffer + (bufferSize / 2),
2712                                       bufferSize / 2, fromFile, toMachine,
2713                                       fileLength, bytesCopied);
2714 
2715                         if (rc != kSTAFOk)
2716                         {
2717                             result = "Unrecoverable read error occurred while"
2718                                 " copying file " + fromFile + " in text mode";
2719                             break;
2720                         }
2721 
2722                         bytesInBuffer += inFile.gcount();
2723                         bytesCopied += inFile.gcount();
2724 
2725                         // Finally, let's check to make sure that this buffer
2726                         // was big enough by finding a newline.  Otherwise,
2727                         // let's run the loop again.
2728 
2729                         if (bytesInBuffer < bufferSize) done = true;
2730 
2731                         i = 0;
2732 
2733                         if (bytesInBuffer > 0)
2734                         {
2735                             i = bytesInBuffer - 1;  // Last NewLine index
2736                             guardIndex = (bufferSize / 2) - eolBufferSize;
2737 
2738                             while (((buffer[i] != eolLastChar) ||
2739                                     !memcmp(buffer + i - eolBufferSize,
2740                                             eolBuffer, eolBufferSize)) &&
2741                                    (i > guardIndex))
2742                             { --i; }
2743                         }
2744 
2745                     } // while ((i == guardIndex) && !done)
2746 
2747                     // We now have the last newline in the buffer
2748 
2749                     if (rc == kSTAFOk)
2750                     {
2751                         if (!done)
2752                         {
2753                             connection->writeUInt(kSTAFFSContinueCopy);
2754 
2755                             connection->writeString(
2756                                 STAFString(buffer, i + 1,
2757                                            STAFString::kCurrent));
2758 
2759                             memmove(buffer, buffer + i + 1,
2760                                     bufferSize - i - 1);
2761 
2762                             readOffset = bufferSize - i - 1;
2763                         }
2764                         else
2765                         {
2766                             connection->writeUInt(kSTAFFSContinueCopy);
2767 
2768                             connection->writeString(
2769                                 STAFString(buffer, bytesInBuffer,
2770                                            STAFString::kCurrent));
2771                         }
2772 
2773                         gFSCopyManagerPtr->updateFileCopy(
2774                             copyDataPtr, bytesCopied);
2775                     }
2776                 } // while (!done)
2777 
2778                 delete[] eolBuffer;
2779 
2780             } // fileLength > 0
2781 
2782             connection->writeUInt(kSTAFFSFinishedCopy);
2783 
2784             inFile.close();
2785 
2786             if (rc == kSTAFOk)
2787             {
2788                 // Read an ack, so that we know the file is closed and
2789                 // if the file was copied successfully
2790                 rc = connection->readUInt();
2791             }
2792         }
2793         else if (doText)
2794         {
2795             // Text file copy without codepage conversion
2796             // Perform a text transfer with conversion of eol chars without a
2797             // codepage conversion. It runs considerably faster than the
2798             // codepage conversion transfer.
2799             // - This is the type of transfer that will occur if the NOCONVERT
2800             //   option is enabled
2801             // - This is the type of transfer that will occur if the codepages
2802             //   are the same and a text transfer has been specified
2803 
2804             gFSCopyManagerPtr->updateFileCopy1(
2805                 copyDataPtr, fileLength, kSTAFFSTextNoConvert);
2806 
2807             STAFString transferString;
2808             int bufferSize = 3000;
2809             unsigned int transferLen = 0;
2810             char *buffer = new char[bufferSize];
2811             unsigned int bytesCopied = 0;
2812 
2813             connection->writeString(currentEOL);
2814             connection->writeUInt(bufferSize);
2815 
2816             while ((fileLength > 0) && (inFile.good()))
2817             {
2818                 transferLen = STAF_MIN(bufferSize, fileLength);
2819 
2820                 rc = readFile(inFile, buffer, transferLen, fromFile,
2821                               toMachine, fileLength, bytesCopied);
2822 
2823                 if (rc != kSTAFOk)
2824                 {
2825                     result = "Unrecoverable read error occurred while"
2826                          " copying file " + fromFile +
2827                          " in text (no codepage convert) mode";
2828                     break;
2829                 }
2830 
2831                 connection->writeUInt(transferLen);
2832                 connection->write(buffer, transferLen);
2833 
2834                 fileLength -= transferLen;
2835                 bytesCopied += transferLen;;
2836                 gFSCopyManagerPtr->updateFileCopy(copyDataPtr, bytesCopied);
2837             }
2838 
2839             connection->writeUInt(kSTAFFSFinishedCopy);
2840             delete[] buffer;
2841 
2842             inFile.close();
2843 
2844             if (rc == kSTAFOk)
2845             {
2846                 // Read an ack, so that we know the file is closed and
2847                 // if the file was copied successfully
2848                 rc = connection->readUInt();
2849             }
2850         }
2851         else if (useNewAPI)
2852         {
2853             // Binary file copy (using new API)
2854 
2855             connection->writeUInt(fileLength);
2856 
2857             unsigned int bytesCopied = 0;
2858 
2859             gFSCopyManagerPtr->updateFileCopy1(
2860                 copyDataPtr, fileLength, kSTAFFSBinary);
2861 
2862             while ((fileLength > 0) && inFile.good())
2863             {
2864                 writeLength = STAF_MIN(sizeof(fileBuffer), fileLength);
2865 
2866                 rc = readFile(inFile, reinterpret_cast<char *>(fileBuffer),
2867                               writeLength, fromFile, toMachine, fileLength,
2868                               bytesCopied);
2869 
2870                 if (rc != kSTAFOk)
2871                 {
2872                     break;
2873                 }
2874 
2875                 connection->write(fileBuffer, writeLength);
2876                 fileLength -= writeLength;
2877                 bytesCopied += writeLength;
2878                 gFSCopyManagerPtr->updateFileCopy(copyDataPtr, bytesCopied);
2879             }
2880 
2881             inFile.close();
2882 
2883             if (rc == kSTAFOk)
2884             {
2885                 // Read an ack, so that we know the file is closed and
2886                 // if the file was copied successfully
2887                 rc = connection->readUInt();
2888             }
2889         }
2890         else
2891         {
2892             // Binary file copy (using old API)
2893 
2894             gFSCopyManagerPtr->updateFileCopy1(
2895                 copyDataPtr, fileLength, kSTAFFSBinary);
2896 
2897             unsigned bytesCopied = 0;
2898 
2899             do
2900             {
2901                 rc = readFile(inFile, reinterpret_cast<char *>(fileBuffer),
2902                               sizeof(fileBuffer), fromFile, toMachine,
2903                               fileLength, bytesCopied);
2904 
2905                 if (rc != kSTAFOk)
2906                 {
2907                     result = "Unrecoverable read error occurred while"
2908                          " copying file " + fromFile +
2909                          " in binary mode";
2910                     break;
2911                 }
2912 
2913                 writeLength = inFile.gcount();
2914                 connection->writeUInt(writeLength);
2915 
2916                 if ((writeLength == 0) && !inFile.eof())
2917                 {
2918                     gFSCopyManagerPtr->remove(copyDataPtr);
2919 
2920                     return STAFServiceResult(kSTAFFileReadError, fromFile);
2921                 }
2922                 else if (writeLength != 0)
2923                 {
2924                     connection->write(fileBuffer, writeLength);
2925                     ack = connection->readUInt();
2926                     if (ack) result = connection->readString();
2927                     fileLength -= writeLength;
2928                     bytesCopied += writeLength;
2929                     gFSCopyManagerPtr->updateFileCopy(copyDataPtr, bytesCopied);
2930                 }
2931             }while ((writeLength != 0) && !ack && inFile.good());
2932 
2933             inFile.close();
2934 
2935             if (ack != kSTAFOk)
2936             {
2937                 gFSCopyManagerPtr->remove(copyDataPtr);
2938 
2939                 return STAFServiceResult(ack, result);
2940             }
2941             else if (writeLength != 0)
2942             {
2943                  // Tell the other side we are finished
2944 
2945                  connection->writeUInt(0);
2946 
2947                   // Read the final ack
2948 
2949                   try
2950                   {
2951                       ack = connection->readUInt();
2952                   }
2953                   catch (STAFConnectionIOException)
2954                   {
2955                       // Older clients will close the connection instead of
2956                       // acking so we need to ignore Connection IO exceptions here
2957 
2958                       ack = kSTAFOk;
2959                   }
2960 
2961                   if (ack != kSTAFOk)
2962                      rc = ack;
2963             }
2964         }
2965     }
2966     catch (STAFConnectionProviderConnectException &e)
2967     {
2968         rc = kSTAFNoPathToMachine;
2969         result = e.getText() + STAFString(": ") + STAFString(e.getErrorCode());
2970     }
2971     catch (STAFConnectionIOException &e)
2972     {
2973         rc = kSTAFCommunicationError;
2974         result = e.getText() + STAFString(": ") + STAFString(e.getErrorCode());
2975     }
2976     catch (STAFException &e)
2977     {
2978         rc = e.getErrorCode();
2979 
2980         if (rc == kSTAFConverterError)
2981         {
2982             result = e.getText() +
2983                 STAFString(": Caught a STAF Converter Error in "
2984                            "STAFFSService::handleCopyFile() while copying "
2985                            "to file ") +
2986                 toFile + " from machine " + fromMachine +
2987                 ". The file contains data that is not valid in the"
2988                 " codepage that STAF is using.  To see the codepage that "
2989                 "STAF is using, check the value of STAF variable "
2990                 "STAF/Config/CodePage.";
2991         }
2992         else
2993         {
2994             result = e.getText() +
2995                 STAFString(": Caught a STAFException in STAFFSService::"
2996                            "handleCopyFile() while copying to file ") +
2997                 toFile + " from machine " + fromMachine;
2998         }
2999     }
3000     catch (...)
3001     {
3002         rc = kSTAFUnknownError;
3003         result = "Caught unknown exception in STAFFSService::handleCopyFile() "
3004             "while copying to file " + toFile + " from machine " +
3005             fromMachine + "\n";
3006     }
3007 
3008     if (addedFSCopyData)
3009     {
3010         // Remove file copy entry from the map
3011         gFSCopyManagerPtr->remove(copyDataPtr);
3012     }
3013 
3014     return STAFServiceResult(rc, result);
3015 }
3016 
3017 
handleCopyDir(const STAFServiceRequest & requestInfo)3018 STAFServiceResult STAFFSService::handleCopyDir(
3019     const STAFServiceRequest &requestInfo)
3020 {
3021     // Verify that the requesting machine/user has at least trust level 4
3022 
3023     IVALIDATE_TRUST(4, "COPY");
3024 
3025     // Parse the request
3026 
3027     STAFCommandParseResultPtr parsedResult = fCopyDirParser.parse(
3028         requestInfo.fRequest);
3029 
3030     if (parsedResult->rc != kSTAFOk)
3031     {
3032         return STAFServiceResult(kSTAFInvalidRequestString,
3033                                  parsedResult->errorBuffer, 0);
3034     }
3035 
3036     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
3037     STAFString            fromMachine    = requestInfo.fEndpoint;
3038     STAFHandle_t          handle         = requestInfo.fHandle;
3039     STAFString            request        = requestInfo.fRequest;
3040     unsigned int          entryTypesUInt = kSTAFFSFile | kSTAFFSDirectory;
3041     STAFFSCaseSensitive_t caseSensitive  = kSTAFFSCaseDefault;
3042     STAFString            toMachine      = fromMachine;
3043     bool recurse      = (parsedResult->optionTimes("RECURSE") > 0);
3044     bool keepEmptyDir = (parsedResult->optionTimes("KEEPEMPTYDIRECTORIES") > 0);
3045     bool onlyDir      = (parsedResult->optionTimes("ONLYDIRECTORIES") > 0);
3046     bool ignoreErrors = (parsedResult->optionTimes("IGNOREERRORS") > 0);
3047     STAFString   namePattern(kUTF8_STAR);
3048     STAFString   extPattern(kUTF8_STAR);
3049     STAFString   fromDir;
3050     STAFString   toDir;
3051     STAFString   typeString;
3052     STAFString   result;
3053     STAFString   ackResult;
3054     STAFString   errorBuffer;
3055 
3056     // Do not want to resolve TODIRECTORY on this machine.  Need to
3057     // resolve on the target machine
3058 
3059     if (parsedResult->optionTimes("TODIRECTORY") != 0)
3060         toDir = parsedResult->optionValue("TODIRECTORY");
3061     else
3062         toDir = parsedResult->optionValue("DIRECTORY");
3063 
3064     if (parsedResult->optionTimes("CASESENSITIVE") != 0)
3065         caseSensitive = kSTAFFSCaseSensitive;
3066     else if (parsedResult->optionTimes("CASEINSENSITIVE") != 0)
3067         caseSensitive = kSTAFFSCaseInsensitive;
3068 
3069     STAFRC_t rc = RESOLVE_STRING_OPTION("DIRECTORY", fromDir);
3070 
3071     if (!rc) rc = RESOLVE_OPTIONAL_STRING_OPTION("TOMACHINE", toMachine);
3072     if (!rc) rc = RESOLVE_OPTIONAL_STRING_OPTION("NAME", namePattern);
3073     if (!rc) rc = RESOLVE_OPTIONAL_STRING_OPTION("EXT", extPattern);
3074 
3075     if (rc) return STAFServiceResult(rc, errorBuffer);
3076 
3077     // Obtain directory path from the value of DIRECTORY
3078     STAFFSPath directoryPath(fromDir);
3079 
3080     STAFFSEntryPtr entry;
3081 
3082     try
3083     {
3084         if (!directoryPath.exists())
3085         {
3086             ackResult = "Directory, " + fromDir + ", does Not Exist";
3087             return STAFServiceResult(kSTAFDoesNotExist, ackResult);
3088         }
3089 
3090         entry = directoryPath.getEntry();
3091 
3092         // Convert the fromDir to the full long path name
3093         // (e.g. with correct file separators, correct case if Windows,
3094         // no unnecessary trailing slashes, etc)
3095 
3096         fromDir = entry->path().asString();
3097     }
3098     catch (STAFBaseOSErrorException &e)
3099     {
3100         result = "Error on Source Directory: " + fromDir +
3101             "\n" + e.getText() + STAFString(": ") + e.getErrorCode();
3102 
3103         return STAFServiceResult(kSTAFBaseOSError, result);
3104     }
3105 
3106     // if the value of DIRECTORY is not a directory, then stop here
3107     if (entry->type() != kSTAFFSDirectory)
3108     {
3109         ackResult = fromDir + " is Not a Directory";
3110         return STAFServiceResult(kSTAFInvalidValue, ackResult);
3111     }
3112 
3113     STAFConnectionPtr connection;
3114     STAFConnectionProviderPtr provider;
3115     unsigned int levelToUse = 0;
3116     unsigned int flags = 0;
3117 
3118     if (parsedResult->optionTimes("FAILIFEXISTS") != 0)
3119         flags |= 0x00000001;
3120     else if (parsedResult->optionTimes("FAILIFNEW") != 0)
3121         flags |= 0x00000002;
3122 
3123     STAFString newEOL = "Native";
3124 
3125     rc = RESOLVE_OPTIONAL_STRING_OPTION("FORMAT", newEOL);
3126     if (rc) return STAFServiceResult(rc, errorBuffer);
3127 
3128     // If interface cycling is not enabled, and toMachine is not 'local',
3129     // and neither interface nor port are specified in toMachine, get the
3130     // originator's interface and prepend to the toMachine value so that
3131     // the connection to the toMachine will use the orginator's interface.
3132 
3133     if ((!gConnectionManagerPtr->getAutoInterfaceCycling()) &&
3134         (!isLocalMachine(toMachine, 1)) &&
3135         (toMachine.find(gSpecSeparator == STAFString::kNPos)) &&
3136         (toMachine.find(kUTF8_AT) == STAFString::kNPos))
3137     {
3138         unsigned int specSepPos = requestInfo.fEndpoint.find(gSpecSeparator);
3139 
3140         if (specSepPos != STAFString::kNPos)
3141         {
3142             STAFString interface = requestInfo.fEndpoint.subString(
3143                 0, specSepPos);
3144 
3145             if (interface != "local")
3146             {
3147                 // Prepend the interface from the originator's endpoint
3148                 toMachine = interface + gSpecSeparator + toMachine;
3149             }
3150         }
3151     }
3152 
3153     /************** Start transferring the directory ***************/
3154 
3155     // This flag indicates if copyDataPtr has been initialized yet by calling
3156     // gFSCopyManagerPtr->add() and passing it copyDataPtr
3157 
3158     bool addedFSCopyData = false;
3159 
3160     STAFFSCopyManager::FSCopyDataPtr copyDataPtr;
3161 
3162     try
3163     {
3164         // Make a connection to the toMachine
3165 
3166         try
3167         {
3168             rc = gConnectionManagerPtr->makeConnection(
3169                 toMachine, provider, connection, result);
3170         }
3171         catch (STAFConnectionProviderConnectException &e)
3172         {
3173             rc = kSTAFNoPathToMachine;
3174             result = e.getText() + STAFString(": ") +
3175                 STAFString(e.getErrorCode());
3176         }
3177 
3178         if ((rc == kSTAFNoPathToMachine) &&
3179             (toMachine.find(kUTF8_AT) == STAFString::kNPos))
3180         {
3181             // Retry connecting to the toMachine, this time appending the
3182             // originator's port to the toMachine
3183 
3184             unsigned int atPos = requestInfo.fEndpoint.find(kUTF8_AT);
3185 
3186             if (atPos != STAFString::kNPos)
3187             {
3188                 toMachine = toMachine + requestInfo.fEndpoint.subString(atPos);
3189 
3190                 rc = gConnectionManagerPtr->makeConnection(
3191                     toMachine, provider, connection, result);
3192             }
3193         }
3194 
3195         if (rc) return STAFServiceResult(rc, result);
3196 
3197         // First, lets try the new API
3198 
3199         connection->writeUInt(kSTAFDirectoryCopyAPI);  // API number
3200         connection->writeUInt(0);    // Dummy level
3201 
3202         STAFRC_t ack = connection->readUInt();
3203 
3204         // The client don't support the new API
3205         if (ack != kSTAFOk)
3206             return STAFServiceResult(ack, "No Support for Directory Copy API");
3207 
3208         // Now find out the specific level to use
3209         unsigned int minLevel = 1;
3210         unsigned int maxLevel = 4;
3211 
3212         connection->writeUInt(minLevel);
3213         connection->writeUInt(maxLevel);
3214 
3215         levelToUse = connection->readUInt();
3216 
3217         if (levelToUse == 0)
3218             return STAFServiceResult(kSTAFInvalidAPILevel,
3219                                      "Invalid API Level");
3220 
3221         int numTextExtentions = parsedResult->optionTimes("TEXTEXT");
3222 
3223         // This prevents attempts to transfer if invalid api
3224 
3225         if ((levelToUse < 2) && ( numTextExtentions != 0))
3226             return STAFServiceResult(kSTAFInvalidAPILevel,
3227                                      "Invalid API Level");
3228 
3229         STAFString currentEOL;
3230 
3231         // Determine default current EOL
3232 
3233         STAFConfigInfo sysInfo;
3234         STAFString_t eBuff;
3235         unsigned int osRC;
3236 
3237         if (STAFUtilGetConfigInfo(&sysInfo, &eBuff, &osRC) != kSTAFOk)
3238         {
3239             // Kill transfer
3240             return STAFServiceResult(
3241                 kSTAFBaseOSError,
3242                 STAFString("Get current EOL failure.  ") +
3243                 STAFString(eBuff, STAFString::kShallow) +
3244                 ", RC: " + osRC);
3245         }
3246 
3247         currentEOL = sysInfo.lineSeparator;
3248 
3249         // Make sure not doing a cyclic copy (where the source includes the
3250         // destination) when copying a directory using the RECURSE option
3251         // to the local machine
3252 
3253         if (recurse)
3254         {
3255             // Check if toMachine is the same as the local (from) machine
3256 
3257             // XXX: It would be great if we had a better way to check if
3258             // the toMachine is the same machine as the local machine.
3259             // Problems with the current method of submitting a MISC WHOAMI
3260             // request to the toMachine include:
3261             // 1) It only knows if the toMachine is running the same instance
3262             //    of STAF since it uses the STAF UUID.  If we stored a Global
3263             //    UUID in shared memory, then we could compare the toMachine's
3264             //    Global UUID with the local machine's Global UUID instead.
3265             // 2) It submits a request to the toMachine.  We already have a
3266             //    connection to toMachine so we could change it to read the
3267             //    toMachine's Global UUID via this existing connection.
3268 
3269             bool localToMachine = false;
3270 
3271             if (isLocalMachine(toMachine, 1))
3272             {
3273                 localToMachine = true;
3274             }
3275             else
3276             {
3277                 // Submit a WhoAmI request to the toMachine to see if it's the
3278                 // local machine
3279 
3280                 STAFResultPtr whoamiResult = gSTAFProcHandlePtr->submit(
3281                     toMachine, "MISC", "WHOAMI");
3282 
3283                 if (whoamiResult->rc == kSTAFOk)
3284                 {
3285                     // Unmarshall the result and get if isLocalRequest is "Yes"
3286 
3287                     if (whoamiResult->rc == kSTAFOk)
3288                     {
3289                         STAFString isLocalMachine = whoamiResult->resultObj->
3290                             get("isLocalRequest")->asString();
3291 
3292                         if (isLocalMachine == "Yes")
3293                         {
3294                             localToMachine = true;
3295                         }
3296                     }
3297                 }
3298             }
3299 
3300             if (localToMachine)
3301             {
3302                 // Resolve any STAF variables in toDir on the local machine
3303                 // (since it hasn't been resolved yet)
3304 
3305                 STAFString resToDir = toDir;
3306 
3307                 STAFResultPtr toDirResult = gSTAFProcHandlePtr->submit(
3308                     "local", "VAR",
3309                     "RESOLVE STRING " + STAFHandle::wrapData(toDir));
3310 
3311                 if (toDirResult->rc == kSTAFOk) resToDir = toDirResult->result;
3312 
3313                 // Convert the toDirectory to the full long path name
3314                 // (e.g. with correct file separators, correct case if Windows,
3315                 // no unnecessary trailing slashes, etc)
3316 
3317                 STAFFSPath toDirPath(resToDir);
3318                 resToDir = toDirPath.setRoot(toDirPath.root()).asString();
3319 
3320                 // Compares the from directory and to directory names in a
3321                 // "normalized" form to make sure that they don't specify the
3322                 // same directory name and to make sure that the fromDir
3323                 // doesn't start with (include) the to directory which would
3324                 // mean it's a cyclic copy which we don't support.
3325 
3326                 unsigned int compareResult = STAFFileSystem::comparePaths(
3327                     resToDir, fromDir);
3328 
3329                 if (compareResult == kSTAFFSDoesIncludePath)
3330                 {
3331                     return STAFServiceResult(
3332                         kSTAFDirectoryCopyError,
3333                         "Cannot perform a cyclic copy (the source includes the "
3334                         "destination)");
3335                 }
3336                 else if (compareResult == kSTAFFSSamePath)
3337                 {
3338                     return STAFServiceResult(
3339                         kSTAFFileWriteError,
3340                         STAFString("Cannot write to directory ") + resToDir +
3341                         " at the same time this request is reading from it");
3342                 }
3343             }
3344         }
3345 
3346         // Add an entry to the FS Copy Map so can list copies in progress
3347 
3348         if (gFSCopyManagerPtr->add(fromDir, toMachine, kSTAFFSCopyFrom,
3349                                    kSTAFFSDirectoryCopy, kSTAFFSBinary,
3350                                    0, copyDataPtr))
3351         {
3352             return STAFServiceResult(
3353                 kSTAFFileReadError,
3354                 STAFString( "Cannot read from directory ") + fromDir +
3355                 " at the same time another copy request is writing to it");
3356         }
3357 
3358         addedFSCopyData = true;
3359 
3360         connection->writeString(*gMachinePtr);
3361         connection->writeString(fromMachine);
3362         connection->writeString(toDir);   // write the root of toDir
3363         connection->writeUInt(flags);
3364 
3365         if (levelToUse > 2)
3366         {
3367             connection->writeUInt(requestInfo.fHandle);
3368 
3369             if (requiresSecureConnection(requestInfo.fAuthenticator) &&
3370                 (provider->getProperty(
3371                     kSTAFConnectionProviderIsSecureProperty) != "1"))
3372             {
3373                 // Don't send authentication data since non-secure
3374                 // connection.  Instead set authenticator to "none" and
3375                 // user to "anonymous" and set authentication data to ""
3376 
3377                 connection->writeString(gNoneString);
3378                 connection->writeString(gAnonymousString);
3379                 connection->writeString("");
3380             }
3381             else
3382             {
3383                 connection->writeString(requestInfo.fAuthenticator);
3384                 connection->writeString(requestInfo.fUserIdentifier);
3385                 connection->writeString(requestInfo.fAuthenticationData);
3386             }
3387 
3388             connection->writeString(requestInfo.fInterface);
3389             connection->writeString(requestInfo.fLogicalInterfaceID);
3390             connection->writeString(requestInfo.fPhysicalInterfaceID);
3391             connection->writeString(requestInfo.fSTAFInstanceUUID);
3392             connection->writeString(*gSTAFInstanceUUIDPtr);
3393         }
3394 
3395         if (levelToUse > 1)
3396         {
3397             connection->writeString(newEOL);
3398         }
3399 
3400         if (levelToUse > 2)
3401         {
3402             // Write request var pool
3403 
3404             STAFVariablePool::VariableMap requestVarMap =
3405                 requestInfo.fRequestVarPool->getVariableMapCopy();
3406 
3407             connection->writeUInt(requestVarMap.size());
3408 
3409             for (STAFVariablePool::VariableMap::iterator requestIter =
3410                  requestVarMap.begin();
3411                  requestIter != requestVarMap.end();
3412                  ++requestIter)
3413             {
3414                 connection->writeString(requestIter->second.name);
3415                 connection->writeString(requestIter->second.value);
3416             }
3417 
3418             // Write source shared var pool
3419 
3420             STAFVariablePool::VariableMap sourceSharedVarMap =
3421                 requestInfo.fSourceSharedVarPool->getVariableMapCopy();
3422 
3423             connection->writeUInt(sourceSharedVarMap.size());
3424 
3425             for (STAFVariablePool::VariableMap::iterator sourceSharedIter =
3426                      sourceSharedVarMap.begin();
3427                  sourceSharedIter != sourceSharedVarMap.end();
3428                  ++sourceSharedIter)
3429             {
3430                 connection->writeString(sourceSharedIter->second.name);
3431                 connection->writeString(sourceSharedIter->second.value);
3432             }
3433         }
3434 
3435         ack = static_cast<STAFRC_t>(connection->readUInt());
3436         ackResult = connection->readString();
3437 
3438         if (ack != kSTAFOk)
3439         {
3440             gFSCopyManagerPtr->remove(copyDataPtr);
3441             return STAFServiceResult(ack, ackResult);
3442         }
3443 
3444         bool doCodepageConvert = false;
3445 
3446         if (levelToUse > 1)
3447         {
3448             newEOL = connection->readString();
3449 
3450             if (numTextExtentions > 0)
3451             {
3452                 doCodepageConvert = true;
3453                 // XXX: If enable NOCONVERT option, write above line with the
3454                 //      following commented line
3455                 //doCodepageConvert = (parsedResult->optionTimes("NOCONVERT") == 0);
3456                 connection->writeUInt(kSTAFFSTextConvert);
3457             }
3458             else
3459             {
3460                 connection->writeUInt(kSTAFFSBinary);
3461             }
3462         }
3463 
3464         if (doCodepageConvert)
3465         {
3466             // If the codepages on both sides are the same, use the text
3467             // noconvert routine which is faster.
3468 
3469             // Get this codepage id
3470             STAFString cPage;
3471             rc = RESOLVE_STRING("{STAF/Config/CodePage}", cPage);
3472 
3473             // Get other codepage id
3474             STAFString otherCPage = connection->readString();
3475 
3476             if (cPage.isEqualTo(otherCPage))
3477             {
3478                 // XXX: Uncomment this line to enable the codepage filter
3479                 //      instead of forcing codepage conversion
3480                 //doCodepageConvert = false;
3481             }
3482         }
3483 
3484         // Add the text extensions to a list
3485 
3486         STAFString resExt;
3487         SET_STAFString TextExtensionList;
3488 
3489         for (int i = 0; i < numTextExtentions; i++)
3490         {
3491             rc = RESOLVE_INDEXED_STRING_OPTION("TEXTEXT", i + 1, resExt);
3492             if (!rc) TextExtensionList.insert(resExt);
3493         }
3494 
3495         // Create a marshalled list of error information when copying files
3496 
3497         STAFObjectPtr mc = STAFObject::createMarshallingContext();
3498         mc->setMapClassDefinition(fErrorInfoClass->reference());
3499         STAFObjectPtr outputList = STAFObject::createList();
3500 
3501         // Start copying files/directories
3502 
3503         if (!recurse)
3504         {
3505             STAFFSEnumPtr fileEnum = entry->enumerate(namePattern, extPattern,
3506                 STAFFSEntryType_t(entryTypesUInt & ~kSTAFFSDirectory),
3507                 kSTAFFSNoSort, caseSensitive);
3508 
3509             if (fileEnum->isValid())
3510             {
3511                 copyDirectory(fileEnum, fromDir, connection,
3512                             TextExtensionList, currentEOL, newEOL,
3513                             doCodepageConvert, levelToUse, caseSensitive,
3514                             outputList, copyDataPtr, toMachine);
3515 
3516                 mc->setRootObject(outputList);
3517                 result = mc->marshall();
3518             }
3519         }
3520         else
3521         {
3522             recurseCopyDir(entry, namePattern, extPattern, fromDir,
3523                           keepEmptyDir, onlyDir, entryTypesUInt,
3524                           connection, caseSensitive, TextExtensionList,
3525                           currentEOL, newEOL, levelToUse, doCodepageConvert,
3526                           outputList, copyDataPtr, toMachine);
3527 
3528             mc->setRootObject(outputList);
3529             result = mc->marshall();
3530         }
3531 
3532         // Stop copying files/directories
3533 
3534         connection->writeUInt(kSTAFFSStopCopy);
3535 
3536         if (!ignoreErrors && (outputList->size() != 0))
3537             rc = kSTAFDirectoryCopyError;
3538         else
3539         {
3540             rc = kSTAFOk;
3541             result = STAFString();
3542         }
3543     }
3544     catch (STAFConnectionProviderException &e)
3545     {
3546         rc = kSTAFNoPathToMachine;
3547         result = e.getText() + STAFString(": ") + STAFString(e.getErrorCode());
3548     }
3549     catch (STAFConnectionIOException &e)
3550     {
3551         rc = kSTAFCommunicationError;
3552         result = e.getText() + STAFString(": ") + STAFString(e.getErrorCode());
3553     }
3554     catch (...)
3555     {
3556         rc = kSTAFDirectoryCopyError;
3557         result = "Caught unknown exception in STAFFSService::handleCopyDir() "
3558             "while copying to directory " + toDir + " from machine " +
3559             fromMachine + "\n";
3560     }
3561 
3562     if (addedFSCopyData)
3563     {
3564         // Remove file copy entry from map keeping track of copies in progress
3565         gFSCopyManagerPtr->remove(copyDataPtr);
3566     }
3567 
3568     return STAFServiceResult(rc, result);
3569 }
3570 
3571 //   If the fileName is not already surrounded in double quotes, enclose it in
3572 //   double quotes if it contains one or more spaces (because that's needed for
3573 //   the OS "move"/"mv" commands).
3574 //
3575 //   For example:  C:\Test 1  -> "C:\Test 1"
3576 //
3577 //   However, on Unix, if the fileName contains one or more wildcards, *, in
3578 //   addition to one or more spaces, then don't enclose the wildcard characters
3579 //   in double quotes because then the shell won't expand them.
3580 //
3581 //   For example: /tmp/Test 1/test*         -> "/tmp/Test 1/test"*
3582 //                /tmp/Test 1/test*.txt     -> "/tmp/Test 1/test"*".txt"
3583 //                /tmp/Test 1/*est*1**.txt* -> "/tmp/Test 1/"*"est"*"1"**".txt"*
3584 //
3585 //   Input:  String containing the file name
3586 //           bool indicating whether or not to check for wildcards (*)
3587 //   Output: String containing the updated file name
quote(STAFString fileName,bool checkForWildCards)3588 STAFString quote(STAFString fileName, bool checkForWildCards)
3589 {
3590     if (fileName.find(sSpace) == STAFString::kNPos)
3591     {
3592         // No need to quote since fileName does not contain any spaces
3593         return fileName;
3594     }
3595     else if (fileName.find(sDoubleQuote, 0, STAFString::kChar) == 0)
3596     {
3597         // The fileName is already in double quotes (starts with ")
3598         return fileName;
3599     }
3600 
3601     if (!checkForWildCards)
3602     {
3603         // Add a double quote at the beginning and at the end of the fileName
3604         return sDoubleQuote + fileName + sDoubleQuote;
3605     }
3606 
3607     #ifdef STAF_OS_TYPE_WIN32
3608     return sDoubleQuote + fileName + sDoubleQuote;
3609     #else
3610     // On Unix, if the fileName contains one or more wildcards, *, in addition
3611     // to one or more spaces, then don't enclose the wildcard characters in
3612     // double quotes because then the shell won't expand them.
3613 
3614     if (fileName.find(sStar, 0, STAFString::kChar) == STAFString::kNPos)
3615     {
3616         return sDoubleQuote + fileName + sDoubleQuote;
3617     }
3618 
3619     // File name contains wildcards and spaces so add quotes around everything
3620     // except the wildcards
3621 
3622     STAFString newName = "";
3623 
3624     unsigned int wildcardIndex = 0;
3625 
3626     // Find first non-wildcard character
3627     unsigned int nonWildcardIndex = fileName.findFirstNotOf(
3628         sStar, STAFString::kChar);
3629 
3630     // Add double quote before next non-wildcard character
3631 
3632     newName += fileName.subString(0, nonWildcardIndex, STAFString::kChar) +
3633         sDoubleQuote;
3634 
3635     while (nonWildcardIndex != STAFString::kNPos)
3636     {
3637         // Find next wildcard
3638 
3639         wildcardIndex = fileName.findFirstOf(
3640             sStar, nonWildcardIndex + 1, STAFString::kChar);
3641 
3642         if (wildcardIndex == STAFString::kNPos)
3643         {
3644             // No more wildcards
3645 
3646             newName += fileName.subString(
3647                 nonWildcardIndex, STAFString::kRemainder, STAFString::kChar) +
3648                 sDoubleQuote;
3649             break;
3650         }
3651 
3652         newName += fileName.subString(
3653             nonWildcardIndex, wildcardIndex - nonWildcardIndex,
3654             STAFString::kChar) +
3655             sDoubleQuote;
3656 
3657         // Find next non-wildcard character
3658 
3659         nonWildcardIndex = fileName.findFirstNotOf(
3660             sStar, wildcardIndex + 1, STAFString::kChar);
3661 
3662         // Add the wildcard(s)
3663 
3664         newName += fileName.subString(
3665             wildcardIndex, nonWildcardIndex - wildcardIndex,
3666             STAFString::kChar);
3667 
3668         if (nonWildcardIndex != STAFString::kNPos)
3669         {
3670             wildcardIndex = nonWildcardIndex - 1;
3671             newName += sDoubleQuote;
3672         }
3673     }
3674 
3675     return newName;
3676     #endif
3677 }
3678 
handleMove(const bool fileSpecified,const STAFServiceRequest & requestInfo)3679 STAFServiceResult STAFFSService::handleMove(
3680     const bool fileSpecified,
3681     const STAFServiceRequest &requestInfo)
3682 {
3683     // This command only supports moving files from one place to another on
3684     // the same machine.  In order to move files to a different machine,
3685     // we would need to either:
3686     // 1) Submit a STAF FS COPY FILE/DIRECTORY request to move the file/
3687     //    directory and then if successful, submit a STAF FS DELETE ENTRY
3688     //    request to remove the source file/directory, or
3689     // 2) Add new STAFProc API(s) to move a file/directory
3690     //
3691     // And we may want additional options to indicate if file(s) to be moved
3692     // are text files in order to handle changing line endings based on the
3693     // operating systeme the file(s) are being moved to and code page changes.
3694 
3695 
3696     // Verify that the requesting machine/user has at least trust level 4
3697 
3698     IVALIDATE_TRUST(4, "MOVE");
3699 
3700     STAFCommandParseResultPtr parsedResult;
3701     STAFString fromOptionName;
3702 
3703     if (fileSpecified)
3704     {
3705         // Parse the MOVE FILE request
3706 
3707         parsedResult = fMoveFileParser.parse(requestInfo.fRequest);
3708 
3709         fromOptionName = "FILE";
3710     }
3711     else
3712     {
3713         // Parse the MOVE DIRECTORY request
3714 
3715         parsedResult = fMoveDirParser.parse(requestInfo.fRequest);
3716 
3717         fromOptionName = "DIRECTORY";
3718     }
3719 
3720     if (parsedResult->rc != kSTAFOk)
3721     {
3722         return STAFServiceResult(kSTAFInvalidRequestString,
3723                                  parsedResult->errorBuffer, 0);
3724     }
3725 
3726     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
3727     STAFRC_t rc = kSTAFOk;
3728     STAFString errorBuffer;
3729     STAFString result;
3730 
3731     // Resolve the value specified for ENTRY
3732 
3733     STAFString fromEntry;
3734 
3735     rc = RESOLVE_STRING_OPTION(fromOptionName, fromEntry);
3736 
3737     if (rc) return STAFServiceResult(rc, errorBuffer);
3738 
3739     // Check if the "FROM" entry exists unless it contains a "*" wildcard
3740     // and it resides on a Unix system
3741 
3742     bool checkIfFromEntryExists = true;
3743 
3744     #ifndef STAF_OS_TYPE_WIN32
3745     if (fromEntry.find("*") != STAFString::kNPos)
3746         checkIfFromEntryExists = false;
3747     #endif
3748 
3749     if (checkIfFromEntryExists)
3750     {
3751         STAFFSPath fromEntryPath(fromEntry);
3752         STAFFSEntryPtr entry;
3753 
3754         try
3755         {
3756             if (!fromEntryPath.exists())
3757             {
3758                 return STAFServiceResult(
3759                     kSTAFDoesNotExist,
3760                     fromOptionName + " '" + fromEntry + "' does not exist");
3761             }
3762 
3763             entry = fromEntryPath.getEntry();
3764 
3765             // Convert the "FROM" entry to the full long path name
3766             // (e.g. with correct file separators, correct case if Windows,
3767             // no unnecessary trailing slashes, etc)
3768 
3769             fromEntry = entry->path().asString();
3770         }
3771         catch (STAFBaseOSErrorException &e)
3772         {
3773             STAFString errMsg = "Error on " + fromOptionName + ": " +
3774                 fromEntry +
3775                 "\n" + e.getText() + STAFString(": ") + e.getErrorCode();
3776 
3777             return STAFServiceResult(kSTAFBaseOSError, errMsg);
3778         }
3779 
3780         // If the FILE option is specified, verify that its value is a file
3781 
3782         if (fileSpecified)
3783         {
3784             // Verify that the value of FILE is a file
3785 
3786             if (entry->type() != kSTAFFSFile)
3787             {
3788                 return STAFServiceResult(
3789                     kSTAFInvalidValue,
3790                     "'" + fromEntry + "' is not a File");
3791             }
3792         }
3793         else
3794         {
3795             // Verify that the value of DIRECTORY is a directory
3796 
3797             if (entry->type() != kSTAFFSDirectory)
3798             {
3799                 return STAFServiceResult(
3800                     kSTAFInvalidValue,
3801                     "'" + fromEntry + "' is not a Directory");
3802             }
3803         }
3804 
3805     }
3806 
3807     // Resolve the value specified for the "TO" option
3808 
3809     STAFString toOptionName;
3810 
3811     if (fileSpecified)
3812     {
3813         if (parsedResult->optionTimes("TOFILE") != 0)
3814             toOptionName = "TOFILE";
3815         else
3816             toOptionName = "TODIRECTORY";
3817     }
3818     else
3819     {
3820         toOptionName = "TODIRECTORY";
3821     }
3822 
3823     STAFString toEntry;
3824 
3825     rc = RESOLVE_STRING_OPTION(toOptionName, toEntry);
3826 
3827     if (rc) return STAFServiceResult(rc, errorBuffer);
3828 
3829     if ((fromOptionName == "FILE") && (toOptionName == "TOFILE"))
3830     {
3831         // When moving from a file to a file, if the TOFILE already exists,
3832         // verify that TOFILE is a file
3833 
3834         STAFFSPath toEntryPath(toEntry);
3835         STAFFSEntryPtr entry;
3836 
3837         try
3838         {
3839             if (toEntryPath.exists())
3840             {
3841                 // Verify that it is a file
3842 
3843                 entry = toEntryPath.getEntry();
3844 
3845                 // Convert the toDir to the full long path name (e.g. with
3846                 // correct file separators, correct case if Windows, no
3847                 // unnecessary trailing slashes, etc)
3848 
3849                 toEntry = entry->path().asString();
3850 
3851                 // Verify that the value of FILE is a file
3852 
3853                 if (entry->type() != kSTAFFSFile)
3854                 {
3855                     return STAFServiceResult(
3856                         kSTAFInvalidValue, "'" + toEntry + "' is not a File");
3857                 }
3858             }
3859         }
3860         catch (STAFBaseOSErrorException &e)
3861         {
3862             result = "Error on " + toOptionName + ": " + toEntry +
3863                 "\n" + e.getText() + STAFString(": ") + e.getErrorCode();
3864 
3865             return STAFServiceResult(kSTAFBaseOSError, result);
3866         }
3867     }
3868     else if ((fromOptionName == "FILE") && (toOptionName == "TODIRECTORY"))
3869     {
3870         // Moving from a file to a directory.
3871         // Verify that TODIRECTORY is an existing directory
3872 
3873         STAFFSPath toDirPath(toEntry);
3874         STAFFSEntryPtr entry;
3875 
3876         try
3877         {
3878             if (!toDirPath.exists())
3879             {
3880                 return STAFServiceResult(
3881                     kSTAFDoesNotExist,
3882                     toOptionName + " '" + toEntry + "' does not exist");
3883             }
3884 
3885             entry = toDirPath.getEntry();
3886 
3887             // Convert the toDir to the full long path name
3888             // (e.g. with correct file separators, correct case if Windows,
3889             // no unnecessary trailing slashes, etc)
3890 
3891             toEntry = entry->path().asString();
3892 
3893             // If the value of TODIRECTORY is not a directory, return
3894             // an error
3895 
3896             if (entry->type() != kSTAFFSDirectory)
3897             {
3898                 result = "'" + toEntry + "' is not a Directory";
3899                 return STAFServiceResult(kSTAFInvalidValue, result);
3900             }
3901         }
3902         catch (STAFBaseOSErrorException &e)
3903         {
3904             result = "Error on " + toOptionName + ": " + toEntry +
3905                 "\n" + e.getText() + STAFString(": ") + e.getErrorCode();
3906 
3907             return STAFServiceResult(kSTAFBaseOSError, result);
3908         }
3909     }
3910     else if ((fromOptionName == "DIRECTORY") &&
3911              (toOptionName == "TODIRECTORY"))
3912     {
3913         // When moving from a directory to a directory, if the TODIRECTORY
3914         // already exists, verify that TODIRECTORY is a directory
3915 
3916         STAFFSPath toEntryPath(toEntry);
3917         STAFFSEntryPtr entry;
3918 
3919         try
3920         {
3921             if (toEntryPath.exists())
3922             {
3923                 // Verify that it is a directory
3924 
3925                 entry = toEntryPath.getEntry();
3926 
3927                 // Convert the toDir to the full long path name (e.g. with
3928                 // correct file separators, correct case if Windows, no
3929                 // unnecessary trailing slashes, etc)
3930 
3931                 toEntry = entry->path().asString();
3932 
3933                 // If the value of TODIRECTORY is not a directory, return
3934                 // an error
3935 
3936                 if (entry->type() != kSTAFFSDirectory)
3937                 {
3938                     result = "'" + toEntry + "' is not a Directory";
3939                     return STAFServiceResult(kSTAFInvalidValue, result);
3940                 }
3941             }
3942         }
3943         catch (STAFBaseOSErrorException &e)
3944         {
3945             result = "Error on " + toOptionName + ": " + toEntry +
3946                 "\n" + e.getText() + STAFString(": ") + e.getErrorCode();
3947 
3948             return STAFServiceResult(kSTAFBaseOSError, result);
3949         }
3950     }
3951 
3952     // To move a file/directory on the same machine, use a "move" command
3953     // for the operating system so that it is faster and file permissions are
3954     // retained
3955 
3956     #ifdef STAF_OS_TYPE_WIN32
3957     STAFString moveCommand = "move /Y";
3958     bool checkForWildcards = false;
3959     #else
3960     STAFString moveCommand = "mv -f";
3961     bool checkForWildcards = true;
3962     #endif
3963 
3964     // If the from/to entry names are not already surrounded in double quotes,
3965     // enclose them in double quotes if they contain one or more spaces
3966     // (because that's needed for the OS "move"/"mv" commands).
3967 
3968     moveCommand += " "  + quote(fromEntry, checkForWildcards) + " " +
3969         quote(toEntry, false);
3970 
3971     // Use a local PROCESS START WAIT request to run the "move" comamnd
3972 
3973     STAFString request = "START SHELL COMMAND " +
3974         STAFHandle::wrapData(moveCommand) +
3975         " RETURNSTDOUT STDERRTOSTDOUT WAIT";
3976 
3977     STAFResultPtr res = gSTAFProcHandlePtr->submit(
3978         "local", "PROCESS", request);
3979 
3980     if (res->rc != kSTAFOk)
3981     {
3982         // Should not have a problem submitting the PROCESS START request
3983         return STAFServiceResult(res->rc, res->result);
3984     }
3985 
3986     // Check if the process RC is 0
3987 
3988     if (res->resultObj->type() == kSTAFMapObject)
3989     {
3990         STAFString processRC = res->resultObj->get("rc")->asString();
3991 
3992         if (processRC != "0")
3993         {
3994             rc = kSTAFMoveError;
3995         }
3996 
3997         STAFObjectPtr fileList = res->resultObj->get("fileList");
3998 
3999         if (fileList->type() == kSTAFListObject)
4000         {
4001             // Get stdout/stderr information to return in the result
4002 
4003             for (STAFObjectIteratorPtr iter = fileList->iterate();
4004                  iter->hasNext();)
4005             {
4006                 STAFObjectPtr fileMap = iter->next();
4007 
4008                 if (fileMap->type() == kSTAFMapObject)
4009                 {
4010                     if (fileMap->get("rc")->asString() == "0")
4011                     {
4012                         result += fileMap->get("data")->asString().strip();
4013                     }
4014                     else
4015                     {
4016                         result += "Error getting stdout/stderr for " +
4017                             moveCommand + ", rc=" +
4018                             fileMap->get("rc")->asString();
4019                     }
4020                 }
4021             }
4022         }
4023     }
4024     else
4025     {
4026         // Should never happen
4027         rc = kSTAFMoveError;
4028         result += "Error accessing result from command: " + moveCommand;
4029     }
4030 
4031     return STAFServiceResult(rc, result);
4032 }
4033 
handleGet(const STAFServiceRequest & requestInfo)4034 STAFServiceResult STAFFSService::handleGet(
4035     const STAFServiceRequest &requestInfo)
4036 {
4037     // Parse the request
4038 
4039     STAFCommandParseResultPtr parsedResult =
4040                               fGetParser.parse(requestInfo.fRequest);
4041 
4042     if (parsedResult->rc != kSTAFOk)
4043     {
4044         return STAFServiceResult(kSTAFInvalidRequestString,
4045                                  parsedResult->errorBuffer, 0);
4046     }
4047 
4048     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
4049     STAFRC_t rc = kSTAFOk;
4050     STAFString errorBuffer;
4051     STAFString result;
4052 
4053     if (parsedResult->optionTimes("FILE") != 0)
4054     {
4055         // GET FILE request
4056 
4057         // Verify that the requesting machine/user has at least trust level 4
4058 
4059         IVALIDATE_TRUST(4, "GET FILE");
4060 
4061         // Determine if Text or Binary
4062 
4063         bool convertToText = true;         // Defaults to text
4064 
4065         if (parsedResult->optionTimes("BINARY") != 0) convertToText = false;
4066 
4067         // Determine the format
4068 
4069         STAFString format;
4070 
4071         if (parsedResult->optionTimes("FORMAT") != 0)
4072         {
4073             // Resolve the value specified for the FORMAT option
4074 
4075             STAFString unresFormat = parsedResult->optionValue("FORMAT");
4076 
4077             rc = RESOLVE_STRING_OPTION("FORMAT", format);
4078 
4079             if (rc) return STAFServiceResult(rc, errorBuffer);
4080 
4081             // If Binary, verify that the format specified is Hex
4082 
4083             if (!convertToText && format.toUpperCase() != "HEX")
4084                 return STAFServiceResult(kSTAFInvalidValue, format);
4085         }
4086         else
4087         {
4088             if (convertToText)
4089                 format = "NATIVE";  // Default Text format is Native
4090             else
4091                 format = "HEX";     // Default Binary format is Hex
4092         }
4093 
4094         // Check if the undocumented TEST option was specified
4095 
4096         bool testFlag = false;
4097 
4098         if (parsedResult->optionTimes("TEST") != 0)
4099             testFlag = true;
4100 
4101         // Resolve the value specified for FILE
4102 
4103         STAFString file;
4104 
4105         rc = RESOLVE_STRING_OPTION("FILE", file);
4106 
4107         if (rc) return STAFServiceResult(rc, errorBuffer);
4108 
4109         // Check if the file exists
4110 
4111         STAFFSPath path(file);
4112 
4113         try
4114         {
4115             if (!path.exists())
4116                 return STAFServiceResult(
4117                     kSTAFDoesNotExist, "File " + file + " does not exist");
4118         }
4119         catch (STAFBaseOSErrorException &e)
4120         {
4121             STAFString errMsg = "Error on file: " + file + "\n" +
4122                 e.getText() + STAFString(": ") + e.getErrorCode();
4123 
4124             return STAFServiceResult(kSTAFBaseOSError, errMsg);
4125         }
4126 
4127         // Get the actual path name and remove any unnecessary trailing slashes
4128         file = path.setRoot(path.root()).asString();
4129 
4130         // Open the file to read it
4131 
4132         fstream inFile(file.toCurrentCodePage()->buffer(),
4133                        ios::in | STAF_ios_binary);
4134 
4135         if (!inFile)
4136             return STAFServiceResult(kSTAFFileOpenError, file);
4137 
4138         // Get the size of the file (upperSize and lowerSize).
4139         // If the file size is < 4G, upperSize will be zero.
4140 
4141         STAFFSEntryPtr entry;
4142 
4143         try
4144         {
4145             entry = path.getEntry();
4146         }
4147         catch (STAFBaseOSErrorException &e)
4148         {
4149             STAFString errMsg = "Error on file: " + file + "\n" +
4150                 e.getText() + STAFString(": ") + e.getErrorCode();
4151 
4152             return STAFServiceResult(kSTAFBaseOSError, errMsg);
4153         }
4154 
4155         unsigned int upperSize = entry->size().first;
4156         unsigned int lowerSize = entry->size().second;
4157 
4158         // Check if file size exceeds the maximum that the FS service handles
4159 
4160         if (upperSize > 0)
4161         {
4162             STAFString errMsg = STAFString(
4163                 "File size exceeds the maximum size (") + UINT_MAX +
4164                 ") supported.  File name: " + file;
4165             return STAFServiceResult(kSTAFFileReadError, errMsg);
4166         }
4167 
4168         unsigned int fileLength = lowerSize;
4169 
4170         // Added for debugging memory issues
4171 
4172         if (gResultWarningSize)
4173         {
4174             if (fileLength > gResultWarningSize)
4175             {
4176                 // Log a warning tracepoint message
4177 
4178                 STAFString warningMsg = STAFString(
4179                     "WARNING: Submitting a FS GET FILE request for a large "
4180                     "file (") + fileLength + " bytes) uses a lot of memory.  "
4181                     "STAFFSService::handleGet()";
4182 
4183                 warningMsg += " - Client: " + requestInfo.fMachine +
4184                     ", Handle: " + STAFString(requestInfo.fHandle) +
4185                     ", Handle Name: " + requestInfo.fHandleName +
4186                     ", Request: " + requestInfo.fRequest;
4187 
4188                 STAFTrace::trace(kSTAFTraceWarning, warningMsg);
4189             }
4190         }
4191 
4192         // Determine the maximum return file size
4193 
4194         unsigned int maxFileSize = gMaxReturnFileSize;  // Default
4195 
4196         // Check if variable STAF/MaxReturnFileSize was set to a non-zero
4197         // value.  Resolve this variable using only the originating handle's
4198         // pool associated with this request so create a variable pool that
4199         // only contains the request variable pool.
4200 
4201         const STAFVariablePool *theVarPoolList[] =
4202         {
4203             requestInfo.fRequestVarPool
4204         };
4205 
4206         unsigned int theVarPoolListSize = sizeof(theVarPoolList) /
4207             sizeof(const STAFVariablePool *);
4208 
4209         STAFString sizeString;
4210 
4211         STAFRC_t maxSizeRC = STAFVariablePool::resolve(
4212             "{STAF/MaxReturnFileSize}",
4213             theVarPoolList, theVarPoolListSize, sizeString);
4214 
4215         if (maxSizeRC == kSTAFOk)
4216         {
4217             // Variable STAF/MaxReturnFileSize exists
4218 
4219             // Verify if its size value is valid and convert it to bytes
4220             // if needed
4221 
4222             STAFString_t errorBuffer = 0;
4223             unsigned int maxSize = 0; // 0 means no size limit
4224 
4225             STAFRC_t rc = STAFUtilConvertSizeString(
4226                 sizeString.getImpl(), &maxSize, &errorBuffer);
4227 
4228             if (rc != kSTAFOk)
4229             {
4230                 STAFString errMsg = STAFString(
4231                     "Variable STAF/MaxReturnFileSize in the originating "
4232                     "handle's variable pool for this request is set to "
4233                     "an invalid value: ") + sizeString + " \n" +
4234                     STAFString(errorBuffer, STAFString::kShallow);
4235 
4236                 return STAFServiceResult(kSTAFInvalidValue, errMsg);
4237             }
4238 
4239             if (maxSize != 0)
4240             {
4241                 if ((gMaxReturnFileSize == 0) ||
4242                     (maxSize < gMaxReturnFileSize))
4243                 {
4244                     // Assign the maximum file size based on the
4245                     // STAF/MaxReturnFileSize variable
4246                     maxFileSize = maxSize;
4247                 }
4248             }
4249         }
4250 
4251         // Determine if the file size exceeds the maximum allowed size
4252 
4253         if ((maxFileSize != 0) && (fileLength > maxFileSize))
4254         {
4255             return STAFServiceResult(
4256                 kSTAFMaximumSizeExceeded,
4257                 STAFString("File '") + file + "' size is " + fileLength +
4258                 " bytes which exceeds " + STAFString(maxFileSize) +
4259                 " bytes, the maximum return file size allowed");
4260         }
4261 
4262         // Initialize the output buffer and read in the file
4263 
4264         STAFBuffer<char> buffer(new char[fileLength], STAFBuffer<char>::INIT,
4265                                 STAFBuffer<char>::ARRAY);
4266 
4267         inFile.read(buffer, fileLength);
4268         inFile.close();
4269 
4270         if (!convertToText)
4271         {
4272             // Convert the file contents to a hex format
4273             return convertToHex(buffer, fileLength);
4274         }
4275 
4276         // Don't convert line-endings in the text file if format is "AsIs"
4277 
4278         if (format.toUpperCase() == "ASIS")
4279         {
4280             // If the undocumented TEST option was specified.
4281 
4282             if (testFlag)
4283             {
4284                 // Return the result in hex to verify that the line-ending
4285                 // characters were not converted.
4286                 return convertToHex(buffer, fileLength);
4287             }
4288 
4289             return STAFServiceResult(rc, STAFString(buffer, fileLength));
4290         }
4291 
4292         // Convert the line ending characters in the text file
4293 
4294         return convertLineEndings(buffer, fileLength, format,
4295                                   requestInfo.fEndpoint,
4296                                   requestInfo.fIsLocalRequest, testFlag);
4297     }
4298     else
4299     {
4300         // GET ENTRY request
4301 
4302         // Verify that the requesting machine/user has at least trust level 2
4303 
4304         IVALIDATE_TRUST(2, "GET ENTRY");
4305 
4306         STAFString entryName;
4307 
4308         rc = RESOLVE_STRING_OPTION("ENTRY", entryName);
4309 
4310         if (rc) return STAFServiceResult(rc, errorBuffer);
4311 
4312         STAFFSPath path(entryName);
4313 
4314         try
4315         {
4316             if (!path.exists())
4317             {
4318                 return STAFServiceResult(
4319                     kSTAFDoesNotExist, "Entry " + entryName +
4320                     " does not exist");
4321             }
4322         }
4323         catch (STAFBaseOSErrorException &e)
4324         {
4325             STAFString errMsg = "Error on entry: " + entryName + "\n" +
4326                 e.getText() + STAFString(": ") + e.getErrorCode();
4327 
4328             return STAFServiceResult(kSTAFBaseOSError, errMsg);
4329         }
4330 
4331         STAFFSEntryPtr entry;
4332 
4333         try
4334         {
4335             entry = path.getEntry();
4336         }
4337         catch (STAFBaseOSErrorException &e)
4338         {
4339             STAFString errMsg = e.getText() + STAFString(": ") +
4340                 e.getErrorCode();
4341 
4342             return STAFServiceResult(kSTAFBaseOSError, errMsg);
4343         }
4344 
4345         if (parsedResult->optionTimes("TYPE") != 0)
4346         {
4347             result = getTypeString(entry->type());
4348         }
4349         else if (parsedResult->optionTimes("SIZE") != 0)
4350         {
4351             // Create a marshalled map containing entry size information
4352 
4353             STAFObjectPtr mc = STAFObject::createMarshallingContext();
4354             mc->setMapClassDefinition(fEntrySizeInfoClass->reference());
4355 
4356             STAFObjectPtr entrySizeMap = fEntrySizeInfoClass->createInstance();
4357             entrySizeMap->put("size", STAFString(entry->size64()));
4358             entrySizeMap->put("upperSize", STAFString(entry->size().first));
4359             entrySizeMap->put("lowerSize", STAFString(entry->size().second));
4360 
4361             mc->setRootObject(entrySizeMap);
4362             result = mc->marshall();
4363         }
4364         else if (parsedResult->optionTimes("MODTIME") != 0)
4365         {
4366             result = entry->modTime().asString();
4367         }
4368         else if (parsedResult->optionTimes("LINKTARGET") != 0)
4369         {
4370             if (entry->linkTarget() != "")
4371                 result = entry->linkTarget();
4372             else
4373                 result = sNoneString;
4374         }
4375         else if (parsedResult->optionTimes("CHECKSUM") != 0)
4376         {
4377 #ifdef STAF_USE_SSL
4378 
4379             // Check if the entry specified is a directory and if so, return
4380             // an error as you cannot get the checksum for a directory
4381 
4382             if (entry->type() == kSTAFFSDirectory)
4383             {
4384                 return STAFServiceResult(
4385                     kSTAFInvalidValue,
4386                     "Cannot get the checksum for an entry that is a "
4387                     "directory");
4388             }
4389 
4390             // Default the checksum alogrithm to MD5 if not specified
4391 
4392             STAFString checksumAlgorithm = "MD5";
4393 
4394             // Get the checksum value, if specified, and resolve any STAF
4395             // variables in it
4396 
4397             if (parsedResult->optionValue("CHECKSUM") != "")
4398             {
4399                 rc = RESOLVE_OPTIONAL_STRING_OPTION(
4400                     "CHECKSUM", checksumAlgorithm);
4401             }
4402 
4403             // Compute the checksum for the specified cryptographic hashing
4404             // algorithm (aka digest) using functions provided by OpenSSL
4405 
4406             const EVP_MD *md;
4407 
4408             OpenSSL_add_all_digests();
4409 
4410             md = EVP_get_digestbyname(
4411                 checksumAlgorithm.toUpperCase().toCurrentCodePage()->buffer());
4412 
4413             if (!md)
4414             {
4415                 return STAFServiceResult(
4416                     kSTAFInvalidValue,
4417                     STAFString("Unknown algorithm specified for the "
4418                                "CHECKSUM option: ") + checksumAlgorithm);
4419             }
4420 
4421             // Open the file to read it
4422 
4423             fstream inFile(entryName.toCurrentCodePage()->buffer(),
4424                            ios::in | STAF_ios_binary);
4425 
4426             if (!inFile)
4427             {
4428                 return STAFServiceResult(
4429                     kSTAFFileOpenError,
4430                     STAFString("Cannot open file ") + entryName);
4431             }
4432 
4433             // Get the size of the file (upperSize and lowerSize).
4434             // If the file size is < 4G, upperSize will be zero.
4435 
4436             unsigned int upperSize = entry->size().first;
4437             unsigned int lowerSize = entry->size().second;
4438 
4439             // Check if file size exceeds the maximum that the FS service handles
4440 
4441             if (upperSize > 0)
4442             {
4443                 inFile.close();
4444 
4445                 STAFString errMsg = STAFString(
4446                     "File size exceeds the maximum size (") + UINT_MAX +
4447                     ") supported.  File name: " + entryName;
4448 
4449                 return STAFServiceResult(kSTAFFileReadError, errMsg);
4450             }
4451 
4452             unsigned int fileLength = lowerSize;
4453 
4454             EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
4455             unsigned char md_value[EVP_MAX_MD_SIZE];
4456             unsigned int md_len;
4457 
4458             EVP_DigestInit_ex(mdctx, md, NULL);
4459 
4460             // Read the entire file using a buffer size of 4096 bytes and
4461             // update the digest with the buffer
4462 
4463             unsigned int bytesCopied = 0;
4464             char fileBuffer[4096] = {0};
4465             unsigned int writeLength = 0;
4466 
4467             while ((fileLength > 0) && inFile.good())
4468             {
4469                 writeLength = STAF_MIN(sizeof(fileBuffer), fileLength);
4470 
4471                 rc = readFile(inFile, reinterpret_cast<char *>(fileBuffer),
4472                               writeLength, entryName, "local", fileLength,
4473                               bytesCopied);
4474 
4475                 if (rc != kSTAFOk) break;
4476 
4477                 EVP_DigestUpdate(mdctx, fileBuffer, writeLength);
4478                 fileLength -= writeLength;
4479                 bytesCopied += writeLength;
4480             }
4481 
4482             inFile.close();
4483 
4484             if (rc == kSTAFOk)
4485             {
4486                 // Get the checksum value
4487                 EVP_DigestFinal_ex(mdctx, md_value, &md_len);
4488             }
4489 
4490             EVP_MD_CTX_destroy(mdctx);
4491 
4492             if (rc == kSTAFOk)
4493             {
4494                 // Convert the checksum value to a hexadecimal format
4495                 return convertToHex(reinterpret_cast<char *>(md_value), md_len);
4496             }
4497             else
4498             {
4499                 return STAFServiceResult(
4500                     kSTAFFileReadError,
4501                     STAFString("Error reading file ") + entryName);
4502             }
4503 #else
4504             return STAFServiceResult(
4505                 kSTAFInvalidRequestString, "Cannot specify the CHECKSUM "
4506                 "option because STAF OpenSSL support is not provided");
4507 #endif
4508         }
4509 
4510         return STAFServiceResult(rc, result);
4511     }
4512 }
4513 
4514 
handleList(const STAFServiceRequest & requestInfo)4515 STAFServiceResult STAFFSService::handleList(
4516     const STAFServiceRequest &requestInfo)
4517 {
4518     // Verify that the requesting machine/user has at least trust level 2
4519 
4520     IVALIDATE_TRUST(2, "LIST");
4521 
4522     // Parse the request
4523 
4524     STAFCommandParseResultPtr parsedResult =
4525                               fListParser.parse(requestInfo.fRequest);
4526 
4527     if (parsedResult->rc != kSTAFOk)
4528     {
4529         return STAFServiceResult(kSTAFInvalidRequestString,
4530                                  parsedResult->errorBuffer, 0);
4531     }
4532 
4533     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
4534     STAFString errorBuffer;
4535 
4536     if (parsedResult->optionTimes("SETTINGS") > 0)
4537     {
4538         // LIST SETTINGS
4539 
4540         // Create a marshalling context to represent the settings info
4541 
4542         STAFObjectPtr mc = STAFObject::createMarshallingContext();
4543         mc->setMapClassDefinition(fSettingsClass->reference());
4544         STAFObjectPtr settingsMap = fSettingsClass->createInstance();
4545 
4546         if (gStrictFSCopyTrust)
4547             settingsMap->put("strictFSCopyTrust", "Enabled");
4548         else
4549             settingsMap->put("strictFSCopyTrust", "Disabled");
4550 
4551         mc->setRootObject(settingsMap);
4552 
4553         return STAFServiceResult(kSTAFOk, mc->marshall());
4554     }
4555 
4556     //LIST DIRECTORY <Name> [RECURSE] [LONG [DETAILS] | SUMMARY] [TYPE <Types>]
4557     //     [NAME <Pattern>] [EXT <Pattern>] [CASESENSITIVE | CASEINSENSITIVE]
4558     //     [SORTBYNAME | SORTBYSIZE | SORTBYMODTIME]
4559 
4560     STAFFSSortBy_t        sortBy         = kSTAFFSNoSort;
4561     STAFFSCaseSensitive_t caseSensitive  = kSTAFFSCaseDefault;
4562     unsigned int          entryTypesUInt = kSTAFFSNormal;
4563     bool showLong = (parsedResult->optionTimes("LONG") > 0);
4564     bool details = (parsedResult->optionTimes("DETAILS") > 0);
4565     bool summary = (parsedResult->optionTimes("SUMMARY") > 0);
4566     bool recurse = (parsedResult->optionTimes("RECURSE") > 0);
4567 
4568     STAFString namePattern(kUTF8_STAR);
4569     STAFString extPattern(kUTF8_STAR);
4570     STAFString dir;
4571     STAFString typeString;
4572     STAFRC_t rc = RESOLVE_STRING_OPTION("DIRECTORY", dir);
4573 
4574     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("NAME", namePattern);
4575     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("EXT" , extPattern);
4576     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("TYPE", typeString);
4577 
4578     if (rc) return STAFServiceResult(rc, errorBuffer);
4579 
4580     if (parsedResult->optionTimes("SORTBYNAME") != 0)
4581         sortBy = kSTAFFSSortByName;
4582     else if (parsedResult->optionTimes("SORTBYSIZE") != 0)
4583         sortBy = kSTAFFSSortBySize;
4584     else if (parsedResult->optionTimes("SORTBYMODTIME") != 0)
4585         sortBy = kSTAFFSSortByModTime;
4586 
4587     if (parsedResult->optionTimes("CASESENSITIVE") != 0)
4588         caseSensitive = kSTAFFSCaseSensitive;
4589     else if (parsedResult->optionTimes("CASEINSENSITIVE") != 0)
4590         caseSensitive = kSTAFFSCaseInsensitive;
4591 
4592     if (typeString.toUpperCase() == "ALL")
4593         entryTypesUInt = kSTAFFSAll;
4594     else if (typeString.length() != 0)
4595         entryTypesUInt = getTypeMaskFromString(typeString, true);
4596 
4597     STAFFSPath dirPath(dir);
4598 
4599     try
4600     {
4601         if (!dirPath.exists())
4602             return STAFServiceResult(
4603                 kSTAFDoesNotExist, "Directory " + dir + " does not exist");
4604     }
4605     catch (STAFBaseOSErrorException &e)
4606     {
4607         STAFString errMsg = "Error on Directory: " + dir + "\n" +
4608             e.getText() + STAFString(": ") + e.getErrorCode();
4609 
4610         return STAFServiceResult(kSTAFBaseOSError, errMsg);
4611     }
4612 
4613     // Create a marshalled list of maps containing file information
4614 
4615     STAFObjectPtr mc = STAFObject::createMarshallingContext();
4616 
4617     if (showLong && details)
4618         mc->setMapClassDefinition(fListDetailsInfoClass->reference());
4619     else if (showLong && !details)
4620         mc->setMapClassDefinition(fListLongInfoClass->reference());
4621     else if (summary)
4622         mc->setMapClassDefinition(fListSummaryInfoClass->reference());
4623 
4624     STAFFSEntryPtr dirPathEntry;
4625 
4626     try
4627     {
4628         dirPathEntry = dirPath.getEntry();
4629     }
4630     catch (STAFBaseOSErrorException &e)
4631     {
4632         STAFString errMsg = e.getText() + STAFString(": ") + e.getErrorCode();
4633 
4634         return STAFServiceResult(kSTAFBaseOSError, errMsg);
4635     }
4636 
4637     // Return an error if the DIRECTORY specified is not a directory
4638 
4639     if (dirPathEntry->type() != kSTAFFSDirectory)
4640     {
4641         return STAFServiceResult(
4642             kSTAFInvalidValue, dir + " is not a directory");
4643     }
4644 
4645     if (summary)
4646     {
4647         // Get a summary of the statistics for the directory such as
4648         // total size, number of files in the directory, and number of
4649         // subdirectories
4650 
4651         STAFObjectPtr summaryMap = fListSummaryInfoClass->createInstance();
4652 
4653         STAFUInt64_t dirSize = 0;
4654         STAFUInt64_t numFiles = 0;
4655         STAFUInt64_t numDirectories = 0;
4656 
4657         getDirectorySize(dirPathEntry, namePattern, extPattern,
4658                          entryTypesUInt, caseSensitive, recurse,
4659                          dirSize, numFiles, numDirectories);
4660 
4661         summaryMap->put("name", dirPathEntry->path().asString());
4662         summaryMap->put("size", STAFString(dirSize));
4663         summaryMap->put("numFiles", STAFString(numFiles));
4664         summaryMap->put("numDirectories", STAFString(numDirectories));
4665 
4666         mc->setRootObject(summaryMap);
4667 
4668         return STAFServiceResult(kSTAFOk, mc->marshall());
4669     }
4670 
4671     STAFObjectPtr outputList = STAFObject::createList();
4672 
4673     // Get the actual directory path name
4674 
4675     STAFString rootDir = dirPath.setRoot(dirPath.root()).asString();
4676 
4677 #ifdef STAF_OS_TYPE_WIN32
4678 
4679     // On Windows, need to remove the trailing backslash from a path that
4680     // is a root directory like C:\
4681 
4682     if (rootDir.findFirstNotOf(kUTF8_BSLASH) != STAFString::kNPos)
4683     {
4684         unsigned int lastNonSlashLoc = rootDir.findLastNotOf(kUTF8_BSLASH);
4685 
4686         if (lastNonSlashLoc + 1 != rootDir.length())
4687         {
4688             rootDir = rootDir.subString(
4689                 0, lastNonSlashLoc + 1, STAFString::kChar);
4690         }
4691     }
4692 #endif
4693 
4694     if (!recurse)
4695     {
4696         // List matching entries non-recursively (e.g. don't check
4697         // sub-directories for any matching entries)
4698 
4699         // Get a list of all matching entries
4700 
4701         STAFFSEnumPtr dirEnum = dirPathEntry->enumerate(
4702             namePattern, extPattern, STAFFSEntryType_t(entryTypesUInt),
4703             sortBy, caseSensitive);
4704 
4705         // Iterate through the sorted matching entries and format each entry
4706 
4707         for (; dirEnum->isValid(); dirEnum->next())
4708         {
4709             addListDirectoryEntry(rootDir, dirEnum->entry(),
4710                                   outputList, mc, showLong, details);
4711         }
4712     }
4713     else
4714     {
4715         // List matching entries recursively (e.g. check sub-directories for
4716         // any matching entries as well)
4717 
4718         // Get a list of all matching entries
4719 
4720         STAFFSEntryList entryList;
4721 
4722         recurseListDir(dirPathEntry, namePattern, extPattern,
4723                        entryTypesUInt, caseSensitive, sortBy, entryList);
4724 
4725         // Sort the entries by name in the specified case, or by size, or by
4726         // last modification time.
4727 
4728         switch (sortBy)
4729         {
4730             case kSTAFFSSortByName:
4731             {
4732                 std::sort(entryList.begin(), entryList.end(),
4733                           STAFSortEnumByName(caseSensitive));
4734                 break;
4735             }
4736 
4737             case kSTAFFSSortBySize:
4738             {
4739                 std::sort(entryList.begin(), entryList.end(),
4740                           sortEnumBySize);
4741                 break;
4742             }
4743 
4744             case kSTAFFSSortByModTime:
4745             {
4746                 std::sort(entryList.begin(), entryList.end(),
4747                           sortEnumByModTime);
4748                 break;
4749             }
4750 
4751             default: break;
4752         }
4753 
4754         // Iterate through the sorted matching entries and format each entry
4755 
4756         for (STAFFSEntryList::iterator iter = entryList.begin();
4757              iter != entryList.end(); ++iter)
4758         {
4759             addListDirectoryEntry(rootDir, *iter, outputList, mc,
4760                                   showLong, details);
4761         }
4762     }
4763 
4764     mc->setRootObject(outputList);
4765 
4766     return STAFServiceResult(kSTAFOk, mc->marshall());
4767 }
4768 
4769 
handleListCopyRequests(const STAFServiceRequest & requestInfo)4770 STAFServiceResult STAFFSService::handleListCopyRequests(
4771     const STAFServiceRequest &requestInfo)
4772 {
4773     // Verify that the requesting machine/user has at least trust level 2
4774 
4775     IVALIDATE_TRUST(2, "LIST");
4776 
4777     // Parse the request
4778 
4779     STAFCommandParseResultPtr parsedResult = fListCopyRequestsParser.parse(
4780         requestInfo.fRequest);
4781 
4782     if (parsedResult->rc != kSTAFOk)
4783     {
4784         return STAFServiceResult(kSTAFInvalidRequestString,
4785                                  parsedResult->errorBuffer, 0);
4786     }
4787 
4788     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
4789     STAFString errorBuffer;
4790 
4791     // LIST COPYREQUESTS request
4792 
4793     // Create a marshalling context to represent the list of copy requests
4794 
4795     STAFObjectPtr mc = STAFObject::createMarshallingContext();
4796 
4797     bool longFlag = false;
4798     bool toFlag = false;
4799     bool fromFlag = false;
4800     bool fileFlag = false;
4801     bool directoryFlag = false;
4802     bool binaryFlag = false;
4803     bool textFlag = false;
4804 
4805     if (parsedResult->optionTimes("LONG") > 0) longFlag = true;
4806     if (parsedResult->optionTimes("INBOUND") > 0) toFlag = true;
4807     if (parsedResult->optionTimes("OUTBOUND") > 0) fromFlag = true;
4808     if (parsedResult->optionTimes("DIRECTORY") > 0) directoryFlag = true;
4809 
4810     if (parsedResult->optionTimes("FILE") > 0)
4811     {
4812         fileFlag = true;
4813 
4814         if (parsedResult->optionTimes("BINARY") > 0) binaryFlag = true;
4815         if (parsedResult->optionTimes("TEXT") > 0) textFlag = true;
4816     }
4817 
4818     if (!toFlag && !fromFlag)
4819     {
4820         toFlag = true;
4821         fromFlag = true;
4822     }
4823 
4824     if (!fileFlag && !directoryFlag)
4825     {
4826         fileFlag = true;
4827         directoryFlag = true;
4828     }
4829 
4830     if (!binaryFlag && !textFlag)
4831     {
4832         binaryFlag = true;
4833         textFlag = true;
4834     }
4835 
4836     if (!longFlag)
4837     {
4838         mc->setMapClassDefinition(fCopyRequestClass->reference());
4839     }
4840     else
4841     {
4842         mc->setMapClassDefinition(fCopyFileClass->reference());
4843         mc->setMapClassDefinition(fFileCopyStateClass->reference());
4844         mc->setMapClassDefinition(fCopyDirectoryClass->reference());
4845         mc->setMapClassDefinition(fDirectoryCopyStateClass->reference());
4846     }
4847 
4848     STAFObjectPtr resultList = STAFObject::createList();
4849 
4850     // Read FS Copy Map
4851 
4852     STAFFSCopyManager::FSCopyMap copyMap = gFSCopyManagerPtr->
4853         getFSCopyMapCopy();
4854 
4855     STAFFSCopyManager::FSCopyMap::iterator iter;
4856 
4857     for (iter = copyMap.begin(); iter != copyMap.end(); ++iter)
4858     {
4859         STAFFSCopyManager::FSCopyDataPtr copyData = iter->second;
4860 
4861         STAFObjectPtr resultMap;
4862 
4863         if (!longFlag)
4864             resultMap = fCopyRequestClass->createInstance();
4865         else if (copyData->type == kSTAFFSFileCopy)
4866             resultMap = fCopyFileClass->createInstance();
4867         else
4868             resultMap = fCopyDirectoryClass->createInstance();
4869 
4870         if (copyData->direction == kSTAFFSCopyFrom)
4871         {
4872             if (!fromFlag) continue;
4873 
4874             resultMap->put("io", "Out");
4875         }
4876         else
4877         {
4878             if (!toFlag) continue;
4879 
4880             resultMap->put("io", "In");
4881         }
4882 
4883         if (copyData->type == kSTAFFSFileCopy)
4884         {
4885             if (!fileFlag) continue;
4886 
4887             resultMap->put("type", "F");
4888         }
4889         else
4890         {
4891             if (!directoryFlag) continue;
4892 
4893             resultMap->put("type", "D");
4894         }
4895 
4896         resultMap->put("startTimestamp", copyData->startTimestamp);
4897         resultMap->put("machine", copyData->machine);
4898         resultMap->put("name", copyData->name);
4899 
4900         if (longFlag)
4901         {
4902             // Assign additional information for the copy in progress
4903 
4904             if (copyData->type == kSTAFFSFileCopy)
4905             {
4906                 if (copyData->mode == kSTAFFSBinary)
4907                 {
4908                     if (!binaryFlag) continue;
4909 
4910                     resultMap->put("mode", "Binary");
4911                 }
4912                 else
4913                 {
4914                     if (!textFlag) continue;
4915 
4916                     resultMap->put("mode", "Text");
4917                 }
4918 
4919                 STAFObjectPtr stateMap = fFileCopyStateClass->
4920                     createInstance();
4921 
4922                 if (copyData->fileSize != 0)
4923                 {
4924                     stateMap->put("fileSize", copyData->fileSize);
4925                 }
4926 
4927                 stateMap->put("bytesCopied", copyData->bytesCopied);
4928 
4929                 resultMap->put("state", stateMap);
4930             }
4931             else
4932             {
4933                 STAFObjectPtr stateMap;
4934 
4935                 stateMap = fDirectoryCopyStateClass->createInstance();
4936 
4937                 if (copyData->mode == kSTAFFSBinary)
4938                 {
4939                     stateMap->put("mode", "Binary");
4940                 }
4941                 else
4942                 {
4943                     stateMap->put("mode", "Text");
4944                 }
4945 
4946                 if (copyData->entryName != STAFString(""))
4947                 {
4948                     stateMap->put("name", copyData->entryName);
4949 
4950                     if (copyData->fileSize != 0)
4951                     {
4952                         stateMap->put("fileSize", copyData->fileSize);
4953                     }
4954 
4955                     stateMap->put("bytesCopied", copyData->bytesCopied);
4956                 }
4957 
4958                 resultMap->put("state", stateMap);
4959             }
4960         }
4961 
4962         resultList->append(resultMap);
4963     }
4964 
4965     mc->setRootObject(resultList);
4966 
4967     return STAFServiceResult(kSTAFOk, mc->marshall());
4968 }
4969 
4970 
handleCreate(const STAFServiceRequest & requestInfo)4971 STAFServiceResult STAFFSService::handleCreate(
4972     const STAFServiceRequest &requestInfo)
4973 {
4974     // Verify that the requesting machine/user has at least trust level 4
4975 
4976     IVALIDATE_TRUST(4, "CREATE");
4977 
4978     // Parse the request
4979 
4980     STAFCommandParseResultPtr parsedResult =
4981                               fCreateParser.parse(requestInfo.fRequest);
4982 
4983     if (parsedResult->rc != kSTAFOk)
4984     {
4985         return STAFServiceResult(kSTAFInvalidRequestString,
4986                                  parsedResult->errorBuffer, 0);
4987     }
4988 
4989     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
4990     unsigned int osRC = 0;
4991     STAFFSDirectoryCreateMode_t createMode = kSTAFFSCreateDirOnly;
4992     STAFString dir;
4993     STAFString result;
4994     STAFString errorBuffer;
4995     STAFRC_t rc = RESOLVE_STRING_OPTION("DIRECTORY", dir);
4996 
4997     if (rc) return STAFServiceResult(rc, errorBuffer);
4998 
4999     if (parsedResult->optionTimes("FULLPATH") != 0)
5000         createMode = kSTAFFSCreatePath;
5001 
5002     rc = STAFFSCreateDirectory(dir.getImpl(), createMode, &osRC);
5003 
5004     if (rc == kSTAFBaseOSError)
5005     {
5006         result = STAFString("Could not create directory ") + dir +
5007             STAFString(", OS RC: ") + osRC;
5008     }
5009     else if (rc == kSTAFAlreadyExists)
5010     {
5011         if (parsedResult->optionTimes("FAILIFEXISTS") != 0)
5012         {
5013             result = dir;
5014         }
5015         else
5016         {
5017             // The entry specified for the DIRECTORY option already exists.
5018             // If the entry specified is a directory then don't return an
5019             // error since the FAILIFEXISTS option wan't specified.
5020             // However, if it's not a directory, return an error.
5021 
5022             STAFFSPath dirPath(dir);
5023             STAFFSEntryPtr dirPathEntry;
5024 
5025             try
5026             {
5027                 dirPathEntry = dirPath.getEntry();
5028 
5029                 if (dirPathEntry->type() == kSTAFFSDirectory)
5030                     rc = kSTAFOk;  // Not an error
5031                 else
5032                     result = dir + " already exists, but is not a directory";
5033             }
5034             catch (STAFBaseOSErrorException)
5035             {
5036                 rc = kSTAFOk;  // Ignore exceptions
5037             }
5038         }
5039     }
5040 
5041     return STAFServiceResult(rc, result);
5042 }
5043 
5044 
handleDelete(const STAFServiceRequest & requestInfo)5045 STAFServiceResult STAFFSService::handleDelete(
5046     const STAFServiceRequest &requestInfo)
5047 {
5048     // Parse the request
5049 
5050     STAFCommandParseResultPtr parsedResult = fDeleteParser.parse(
5051         requestInfo.fRequest);
5052 
5053     if (parsedResult->rc != kSTAFOk)
5054     {
5055         return STAFServiceResult(kSTAFInvalidRequestString,
5056                                  parsedResult->errorBuffer, 0);
5057     }
5058 
5059     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
5060     bool children     = (parsedResult->optionTimes("CHILDREN") > 0);
5061     bool recurse      = (parsedResult->optionTimes("RECURSE") > 0);
5062     bool ignoreErrors = (parsedResult->optionTimes("IGNOREERRORS") > 0);
5063     STAFFSCaseSensitive_t caseSensitive  = kSTAFFSCaseDefault;
5064     unsigned int entryTypesUInt = kSTAFFSAll & ~kSTAFFSSpecialDirectory;
5065     STAFString namePattern(kUTF8_STAR);
5066     STAFString extPattern(kUTF8_STAR);
5067     STAFString entryString;
5068     STAFString typeString;
5069     STAFString result;
5070     STAFString errorBuffer;
5071 
5072     // Verify that the requesting machine/user has at least trust level 4
5073     // if RECURSE is not specified and trust level 5 if RECURSE is specified
5074 
5075     if (!recurse)
5076     {
5077         IVALIDATE_TRUST(4, "DELETE");
5078     }
5079     else
5080     {
5081         IVALIDATE_TRUST(5, "DELETE RECURSE");
5082     }
5083 
5084     STAFRC_t rc = RESOLVE_STRING_OPTION("ENTRY", entryString);
5085 
5086     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("NAME", namePattern);
5087     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("EXT" , extPattern);
5088     if (!rc) RESOLVE_OPTIONAL_STRING_OPTION("TYPE", typeString);
5089 
5090     if (rc) return STAFServiceResult(rc, errorBuffer);
5091 
5092     if (parsedResult->optionTimes("CASESENSITIVE") != 0)
5093         caseSensitive = kSTAFFSCaseSensitive;
5094     else if (parsedResult->optionTimes("CASEINSENSITIVE") != 0)
5095         caseSensitive = kSTAFFSCaseInsensitive;
5096 
5097     if (typeString.toUpperCase() == "ALL")
5098         entryTypesUInt = kSTAFFSAll & ~kSTAFFSSpecialDirectory;
5099     else if (typeString.length() != 0)
5100         entryTypesUInt = getTypeMaskFromString(typeString, false);
5101 
5102     STAFFSPath entryPath(entryString);
5103 
5104     try
5105     {
5106         if (!entryPath.exists())
5107             return STAFServiceResult(
5108                 kSTAFDoesNotExist, "Entry " + entryString + " does not exist");
5109     }
5110     catch (STAFBaseOSErrorException &e)
5111     {
5112         STAFString errMsg = "Error on Entry: " + entryString + "\n" +
5113             e.getText() + STAFString(": ") + e.getErrorCode();
5114 
5115         return STAFServiceResult(kSTAFBaseOSError, errMsg);
5116     }
5117 
5118 
5119     STAFFSEntryPtr entry;
5120 
5121     try
5122     {
5123         entry = entryPath.getEntry();
5124     }
5125     catch (STAFBaseOSErrorException &e)
5126     {
5127         STAFString errMsg = e.getText() + STAFString(": ") +
5128             e.getErrorCode();
5129 
5130         return STAFServiceResult(kSTAFBaseOSError, errMsg);
5131     }
5132 
5133     unsigned int osRC = 0;
5134 
5135     // Create a marshalled list of error information deleting a directory
5136 
5137     STAFObjectPtr mc = STAFObject::createMarshallingContext();
5138     mc->setMapClassDefinition(fErrorInfoClass->reference());
5139     STAFObjectPtr outputList = STAFObject::createList();
5140 
5141     if (recurse)
5142     {
5143         if (entry->type() == kSTAFFSDirectory)
5144         {
5145             recurseRemove(entry, namePattern, extPattern, entryTypesUInt,
5146                           caseSensitive, outputList);
5147         }
5148 
5149         if (!children)
5150         {
5151             rc = entry->remove(&osRC);
5152             updateResultString(outputList, entry, rc, osRC);
5153         }
5154 
5155         mc->setRootObject(outputList);
5156         result = mc->marshall();
5157     }
5158     else if (children)
5159     {
5160         if (entry->type() == kSTAFFSDirectory)
5161         {
5162             removeChildren(entry, namePattern, extPattern, entryTypesUInt,
5163                            caseSensitive, outputList);
5164         }
5165 
5166         mc->setRootObject(outputList);
5167         result = mc->marshall();
5168     }
5169     else
5170     {
5171         rc = entry->remove(&osRC);
5172         updateResultString(outputList, entry, rc, osRC);
5173 
5174         mc->setRootObject(outputList);
5175         result = mc->marshall();
5176     }
5177 
5178     if (recurse || children)
5179     {
5180         if (outputList->size() != 0)
5181             rc = kSTAFFileDeleteError;
5182 
5183         if (ignoreErrors)
5184             result = STAFString();
5185     }
5186     else
5187     {
5188         if (rc == kSTAFBaseOSError)
5189             rc = kSTAFFileDeleteError;
5190     }
5191 
5192     return STAFServiceResult(rc, result);
5193 }
5194 
5195 
handleQuery(const STAFServiceRequest & requestInfo)5196 STAFServiceResult STAFFSService::handleQuery(
5197     const STAFServiceRequest &requestInfo)
5198 {
5199     // Verify that the requesting machine/user has at least trust level 2
5200 
5201     IVALIDATE_TRUST(2, "QUERY");
5202 
5203     // Parse the request
5204 
5205     STAFCommandParseResultPtr parsedResult =
5206                               fQueryParser.parse(requestInfo.fRequest);
5207 
5208     if (parsedResult->rc != kSTAFOk)
5209     {
5210         return STAFServiceResult(kSTAFInvalidRequestString,
5211                                  parsedResult->errorBuffer, 0);
5212     }
5213 
5214     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
5215     STAFString entryName;
5216     STAFString result;
5217     STAFString errorBuffer;
5218     STAFRC_t rc = RESOLVE_STRING_OPTION("ENTRY", entryName);
5219 
5220     if (rc) return STAFServiceResult(rc, errorBuffer);
5221 
5222     STAFFSPath path(entryName);
5223 
5224     STAFFSEntryPtr entry;
5225 
5226     try
5227     {
5228         if (!path.exists())
5229             return STAFServiceResult(
5230                 kSTAFDoesNotExist, "Entry " + entryName + " does not exist");
5231 
5232         entry = path.getEntry();
5233     }
5234     catch (STAFBaseOSErrorException &e)
5235     {
5236         STAFString errMsg = "Error on Entry: " + entryName + "\n" +
5237             e.getText() + STAFString(": ") + e.getErrorCode();
5238 
5239         return STAFServiceResult(kSTAFBaseOSError, errMsg);
5240     }
5241 
5242     // Create a marshalled map of information about the specified entry
5243 
5244     STAFObjectPtr mc = STAFObject::createMarshallingContext();
5245     mc->setMapClassDefinition(fQueryInfoClass->reference());
5246 
5247     STAFObjectPtr fileInfoMap = fQueryInfoClass->createInstance();
5248 
5249     fileInfoMap->put("name", entry->path().asString());
5250     fileInfoMap->put("type", getTypeString(entry->type()));
5251     fileInfoMap->put("size", STAFString(entry->size64()));
5252     fileInfoMap->put("upperSize", STAFString(entry->size().first));
5253     fileInfoMap->put("lowerSize", STAFString(entry->size().second));
5254     fileInfoMap->put("lastModifiedTimestamp", entry->modTime().asString());
5255 
5256     if (entry->linkTarget() != "")
5257         fileInfoMap->put("linkTarget", entry->linkTarget());
5258 
5259     mc->setRootObject(fileInfoMap);
5260 
5261     return STAFServiceResult(kSTAFOk, mc->marshall());
5262 }
5263 
5264 
handleSet(const STAFServiceRequest & requestInfo)5265 STAFServiceResult STAFFSService::handleSet(
5266     const STAFServiceRequest &requestInfo)
5267 {
5268     // Verify that the requesting machine/user has at least trust level 5
5269 
5270     IVALIDATE_TRUST(5, "SET");
5271 
5272     // Parse the request
5273 
5274     STAFCommandParseResultPtr parsedResult = fSetParser.parse(
5275         requestInfo.fRequest);
5276 
5277     if (parsedResult->rc != kSTAFOk)
5278     {
5279         return STAFServiceResult(kSTAFInvalidRequestString,
5280                                  parsedResult->errorBuffer, 0);
5281     }
5282 
5283     DEFINE_VAR_POOL_LIST(varPoolList, varPoolListSize, requestInfo);
5284     STAFString strictTrust;
5285     STAFString errorBuffer;
5286     STAFRC_t rc = RESOLVE_STRING_OPTION("STRICTFSCOPYTRUST", strictTrust);
5287 
5288     if (rc) return STAFServiceResult(rc, errorBuffer);
5289 
5290     if (strictTrust.isEqualTo(sEnabled, kSTAFStringCaseInsensitive))
5291     {
5292         // Get exclusive access to gStrictFSCopyTrust
5293 
5294         STAFMutexSemLock lock(sStrictFSCopyTrustSem);
5295         gStrictFSCopyTrust = 1;
5296     }
5297     else if (strictTrust.isEqualTo(sDisabled, kSTAFStringCaseInsensitive))
5298     {
5299         // Get exclusive access to gStrictFSCopyTrust
5300 
5301         STAFMutexSemLock lock(sStrictFSCopyTrustSem);
5302         gStrictFSCopyTrust = 0;
5303     }
5304     else
5305     {
5306         return STAFServiceResult(
5307             kSTAFInvalidValue,
5308             "STRICTFSCOPYTRUST value must be ENABLED or DISABLED.  "
5309             "Invalid value: " + strictTrust);
5310     }
5311 
5312     return STAFServiceResult(kSTAFOk);
5313 }
5314 
5315 
handleHelp(const STAFServiceRequest & requestInfo)5316 STAFServiceResult STAFFSService::handleHelp(
5317     const STAFServiceRequest &requestInfo)
5318 {
5319     // Verify that the requesting machine/user has at least trust level 1
5320 
5321     IVALIDATE_TRUST(1, "HELP");
5322 
5323     return STAFServiceResult(kSTAFOk, sHelpMsg);
5324 }
5325