Main Page | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages | Examples

ArServerFileUtils.cpp

Go to the documentation of this file.
00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 #ifndef WIN32
00027 #include "Aria.h"
00028 #include "ArExport.h"
00029 #include "ArServerFileUtils.h"
00030 
00031 #include <sys/types.h>
00032 #include <dirent.h>
00033 #include <ctype.h>
00034 
00045 AREXPORT bool ArServerFileUtil::squashCase(const char *baseDir, 
00046                                            const char *fileName,
00047                                            char *result,
00048                                            size_t resultLen)
00049 {
00050   DIR *dir;
00051   struct dirent *ent;
00052 
00053   char separator;  
00054 #ifndef WIN32
00055   separator = '/';
00056 #else
00057   separator = '\\';
00058 #endif
00059 
00060   result[0] = '\0';
00061   std::list<std::string> split;
00062   split = splitFileName(fileName);
00063   std::list<std::string>::iterator it;
00064   std::string finding;
00065 
00066   /*
00067   for (it = split.begin(); it != split.end(); it++)
00068   {
00069     printf("@@@@@@@@ %s\n", (*it).c_str());
00070   }
00071   */
00072   it = split.begin();
00073   finding = (*it);
00074   
00075   // how this works is we start at the base dir then read through
00076   // until we find what the next name we need, if entry is a directory
00077   // and we're not at the end of our string list then we change into
00078   // that dir and the while loop keeps going, if the entry isn't a
00079   // directory and matchs and its the last in our string list we've
00080   // found what we want
00081   if ((dir = opendir(baseDir)) == NULL)
00082   {
00083     ArLog::log(ArLog::Normal, 
00084                "ArServerFileUtil: No such directory '%s' for base", 
00085                baseDir);
00086     return false;
00087   }
00088 
00089   while ((ent = readdir(dir)) != NULL)
00090   {
00091     // ignore some of these
00092     if (ent->d_name[0] == '.')
00093     {
00094       //printf("Ignoring %s\n", ent->d_name[0]);
00095       continue;
00096     }
00097     //printf("NAME %s finding %s\n", ent->d_name, finding.c_str());
00098     
00099     // we've found what we were looking for
00100     if (ArUtil::strcasecmp(ent->d_name, finding) == 0)
00101     {
00102       size_t lenOfResult;
00103       lenOfResult = strlen(result);
00104 
00105       // make sure we can put the filename in
00106       if (strlen(ent->d_name) > resultLen - lenOfResult - 2)
00107       {
00108         ArLog::log(ArLog::Normal, 
00109                    "ArServerFileUtil::squashCase: result not long enough");
00110           return false;
00111       }
00112       //printf("Before %s", result);
00113       if (lenOfResult != 0)
00114       {
00115         result[lenOfResult] = separator;
00116         result[lenOfResult+1] = '\0';
00117       }
00118       // put the filename in
00119       strcpy(&result[strlen(result)], ent->d_name);
00120       //printf("after %s\n", result);
00121       // see if we're at the end
00122       it++;
00123       if (it != split.end())
00124       {
00125         //printf("Um.........\n");
00126         finding = (*it);
00127         std::string wholeDir;
00128         wholeDir = baseDir;
00129         wholeDir += result;
00130         //printf("'%s' '%s' '%s'\n", baseDir, result, wholeDir.c_str());
00131         if ((dir = opendir(wholeDir.c_str())) == NULL)
00132         {
00133           ArLog::log(ArLog::Normal, 
00134                      "ArServerFileUtil::squashCase: Error going into %s", 
00135                      result);
00136           return false;
00137         }
00138       }
00139       else
00140       {
00141         //printf("\n########## Got it %s\n", result);
00142         return true;
00143       }
00144     }
00145   }
00146   ArLog::log(ArLog::Verbose, 
00147              "ArServerFileUtil::squashCase: %s doesn't exist in %s", fileName, 
00148              baseDir);
00149   return false;
00150 } 
00151 
00152 AREXPORT bool ArServerFileUtil::getDirectory(const char *fileName, 
00153                                              char *result, size_t resultLen)
00154 {
00155   char separator;  
00156 #ifndef WIN32
00157   separator = '/';
00158 #else
00159   separator = '\\';
00160 #endif
00161   
00162   if (fileName == NULL || fileName[0] == '\0' || resultLen == 0)
00163   {
00164     ArLog::log(ArLog::Normal, "ArServerFileUtil: getDirectory, bad setup");
00165     return false;
00166   }
00167   
00168   // just play in the result buffer
00169   strncpy(result, fileName, resultLen - 1);
00170   // make sure its nulled
00171   result[resultLen - 1] = '\0';
00172   char *toPos;
00173   ArUtil::fixSlashes(result, resultLen);
00174   // see where the last directory is
00175   toPos = strrchr(result, separator);
00176   // if there's no divider it must just be a file name
00177   if (toPos == NULL)
00178   {
00179     result[0] = '\0';
00180     return true;
00181   }
00182   // otherwise just toss a null into the last separator and we're done
00183   else
00184   {    
00185     *toPos = '\0';
00186     return true;
00187   }
00188 }
00189 
00190 AREXPORT bool ArServerFileUtil::getFileName(const char *fileName, 
00191                                          char *result, size_t resultLen)
00192 {
00193   char separator;  
00194 #ifndef WIN32
00195   separator = '/';
00196 #else
00197   separator = '\\';
00198 #endif
00199   
00200   if (fileName == NULL || fileName[0] == '\0' || resultLen == 0)
00201   {
00202     ArLog::log(ArLog::Normal, "ArServerFileUtil: getFileName, bad setup");
00203     return false;
00204   }
00205 
00206   char *str;
00207   size_t fileNameLen = strlen(fileName);
00208   str = new char[fileNameLen + 1];
00209   //printf("0 %s\n", fileName);
00210   // just play in the result buffer
00211   strncpy(str, fileName, fileNameLen);
00212   // make sure its nulled
00213   str[fileNameLen] = '\0';
00214   //printf("1 %s\n", str);
00215 
00216   char *toPos;
00217   ArUtil::fixSlashes(str, fileNameLen + 1);
00218   //printf("2 %s\n", str);
00219   // see where the last directory is
00220   toPos = strrchr(str, separator);
00221   // if there's no divider it must just be a file name
00222   if (toPos == NULL)
00223   {
00224     // copy the filename in and make sure it has a null
00225     strncpy(result, str, resultLen - 1);
00226     result[resultLen - 1] = '\0';
00227     //printf("3 %s\n", result);
00228     return true;
00229   }
00230   // otherwise take the section from that separator to the end
00231   else
00232   {
00233     strncpy(result, &str[toPos - str + 1], resultLen - 2);
00234     result[resultLen - 1] = '\0';
00235     //printf("4 %s\n", result);
00236 
00237     return true;
00238   }
00239 }
00240 
00244 std::list<std::string> ArServerFileUtil::splitFileName(
00245         const char *fileName)
00246 {
00247   std::list<std::string> split;
00248   if (fileName == NULL)
00249     return split;
00250 
00251   char separator;  
00252 #ifndef WIN32
00253   separator = '/';
00254 #else
00255   separator = '\\';
00256 #endif
00257 
00258   size_t len;
00259   size_t i;
00260   size_t last;
00261   bool justSepped;
00262   char entry[2048];
00263   for (i = 0, justSepped = false, last = 0, len = strlen(fileName); 
00264        ; 
00265        i++)
00266   {
00267     
00268     if ((fileName[i] == separator && !justSepped) 
00269         || fileName[i] == '\0' || i >= len)
00270     {
00271       if (i - last > 2047)
00272       {
00273         ArLog::log(ArLog::Normal, "ArServerFileUtil::splitFileName: some directory or file too long");
00274       }
00275       if (!justSepped)
00276       {
00277         strncpy(entry, &fileName[last], i - last);
00278         entry[i-last] = '\0';
00279         split.push_back(entry);
00280         justSepped = true;
00281       }
00282       if (fileName[i] == '\0' || i >= len)
00283         return split;
00284     }
00285     else if (fileName[i] == separator && justSepped)
00286     {
00287       justSepped = true;
00288       last = i;
00289     }
00290     else if (fileName[i] != separator && justSepped)
00291     {
00292       justSepped = false;
00293       last = i;
00294     }
00295   }
00296   ArLog::log(ArLog::Normal, "ArServerFileUtil::splitFileName: file str ('%s') happened weird", fileName);
00297   return split;
00298 }
00299 
00300 AREXPORT ArServerFileLister::ArServerFileLister(ArServerBase *server, 
00301                                                 const char *topDir) :
00302   myGetDirListingCB(this, &ArServerFileLister::getDirListing)
00303 {
00304   myServer = server;
00305   myServer->addData("getDirListing", 
00306                     "Gets the directory listing of a given directory (from the current working directory), you should probably use a class from ArClientFileUtils instead of calling this directly",
00307                     &myGetDirListingCB, "string: directory to get listing of",
00308                     "ubyte2: return code, 0 = good, 1 = tried to go outside allowed area, 2 = no such directory (or can't read); string: directoryListed;  IF return was 0 then ubyte2: numDirectories; repeating numDirectories (string: name; ubyte4: access_time; ubyte4: modified_time); ubyte2: numFiles; repeating numFiles (string: name; ubyte4: access_time; ubyte4: modified_time);", 
00309                     "FileAccess", "RETURN_SINGLE");
00310 
00311   // snag our base dir and make sure we have enough room for a /
00312   strncpy(myBaseDir, topDir, sizeof(myBaseDir) - 2);
00313   myBaseDir[sizeof(myBaseDir) - 2] = '\0';
00314   // make sure it has a slash
00315   ArUtil::appendSlash(myBaseDir, sizeof(myBaseDir));
00316   // make sure the slashes go the right direction
00317   ArUtil::fixSlashes(myBaseDir, sizeof(myBaseDir));  
00318 }
00319 
00320 AREXPORT ArServerFileLister::~ArServerFileLister()
00321 {
00322 
00323 }
00324 
00325 AREXPORT void ArServerFileLister::getDirListing(ArServerClient *client,
00326                                                 ArNetPacket *packet)
00327 {
00328   ArNetPacket sendPacket;
00329   size_t ui;
00330   size_t len;
00331 
00332   bool printing = false;
00333 
00334   char directory[2048];
00335   packet->bufToStr(directory, sizeof(directory));
00336   
00337   if (printing)
00338     printf("'%s' requested\n", directory);
00339   len = strlen(directory);
00340   // first advance to the first non space
00341   for (ui = 0; 
00342        ui < len && directory[ui] != '\0' && isspace(directory[ui]); 
00343        ui++);
00344 
00345   char *dirStr = new char[len + 3];
00346 
00347   // now copy in the rest
00348   strncpy(dirStr, directory, len - ui);
00349   // make sure its null terminated
00350   dirStr[len - ui] = '\0';
00351     
00352   if (dirStr[0] == '/' || dirStr[0] == '~' ||
00353       dirStr[0] == '\\' || strstr(dirStr, "..") != NULL)
00354   {
00355     ArLog::log(ArLog::Normal, "ArServerFileLister: '%s' tried to access outside allowed area", dirStr);
00356     delete[] dirStr;
00357     sendPacket.uByte2ToBuf(1);
00358     // put in the directory name
00359     sendPacket.strToBuf(directory);
00360     if (client != NULL)
00361       client->sendPacketTcp(&sendPacket);
00362     return;
00363   }
00364 
00365   // if its not empty make sure its set up right
00366   if (strlen(dirStr) > 0)
00367   {
00368     // make sure it has a slash
00369     ArUtil::appendSlash(dirStr, len + 2);
00370     // make sure the slashes go the right direction
00371     ArUtil::fixSlashes(dirStr, len + 2);
00372   }
00373 
00374   // put our base and where we want to go together
00375   std::string wholeDir;
00376   wholeDir = myBaseDir;
00377   wholeDir += dirStr;
00378 
00379   DIR *dir;
00380   struct dirent *ent;
00381   struct stat statBuf;
00382   std::set<std::string, ArStrCaseCmpOp> dirs;
00383   std::map<std::string, ArTypes::UByte4> dirToATime;
00384   std::map<std::string, ArTypes::UByte4> dirToMTime;
00385   std::map<std::string, ArTypes::UByte4> dirToSize;
00386   std::set<std::string, ArStrCaseCmpOp> files;
00387   std::map<std::string, ArTypes::UByte4> fileToATime;
00388   std::map<std::string, ArTypes::UByte4> fileToMTime;
00389   std::map<std::string, ArTypes::UByte4> fileToSize;
00390   std::set<std::string, ArStrCaseCmpOp>::iterator it;
00391   std::string str;
00392   
00393   if ((dir = opendir(wholeDir.c_str())) == NULL)
00394   {
00395     ArLog::log(ArLog::Normal, "ArServerFileLister: No such directory '%s' from base '%s' plus directory '%s'", 
00396                directory, myBaseDir, dirStr);
00397     delete[] dirStr;
00398     sendPacket.uByte2ToBuf(2);
00399     // put in the directory name
00400     sendPacket.strToBuf(directory);
00401     if (client != NULL)
00402       client->sendPacketTcp(&sendPacket);
00403     return;
00404   }
00405   while ((ent = readdir(dir)) != NULL)
00406   {
00407     // this works because if the first one goes it short circuits the second
00408     if ((it = dirs.find(ent->d_name)) != dirs.end() || 
00409         (it = files.find(ent->d_name)) != files.end())
00410     {
00411       ArLog::log(ArLog::Normal, 
00412                  "ArServerFileLister: %s duplicates '%s'", 
00413                  ent->d_name, (*it).c_str());
00414       continue;
00415     }      
00416     if (ent->d_name[0] == '.')
00417       continue;
00418     str = wholeDir.c_str();
00419     str += ent->d_name;
00420     if (printing)
00421       printf("name %s\n", str.c_str());
00422     if (stat(str.c_str(), &statBuf) == 0)
00423     {
00424       if (S_ISREG(statBuf.st_mode))
00425       {
00426         files.insert(ent->d_name);
00427         fileToATime[ent->d_name] = statBuf.st_atime;
00428         fileToMTime[ent->d_name] = statBuf.st_mtime;
00429         fileToSize[ent->d_name] = statBuf.st_size;
00430         //printf("File %s\n", ent.d_name);
00431       }
00432       if (S_ISDIR(statBuf.st_mode))
00433       {
00434         dirs.insert(ent->d_name);
00435         dirToATime[ent->d_name] = statBuf.st_atime;
00436         dirToMTime[ent->d_name] = statBuf.st_mtime;
00437         dirToSize[ent->d_name] = statBuf.st_size;
00438         //printf("Dir %s\n", ent.d_name);
00439       }
00440     }
00441     else
00442     {
00443       ArLog::log(ArLog::Normal, "Cannot stat %s in %s", ent->d_name, wholeDir.c_str());
00444     }
00445   }
00446   // we got here so the return is 0 (good)
00447   sendPacket.uByte2ToBuf(0);
00448   // put in the directory name
00449   sendPacket.strToBuf(directory);
00450   if (printing)
00451     printf("Sending %s\n", directory);
00452   if (printing)
00453     printf("Dirs:\n");
00454   // put in how many directories we have
00455   sendPacket.uByte2ToBuf(dirs.size());
00456   for (it = dirs.begin(); it != dirs.end(); it++)
00457   {
00458     sendPacket.strToBuf((*it).c_str());
00459     sendPacket.uByte4ToBuf(dirToATime[(*it)]);
00460     sendPacket.uByte4ToBuf(dirToMTime[(*it)]);
00461     sendPacket.uByte4ToBuf(dirToSize[(*it)]);
00462     
00463   }
00464   
00465   if (printing)
00466   {
00467     printf("\n");
00468     printf("Files:\n");
00469   }
00470   sendPacket.uByte2ToBuf(files.size());
00471   for (it = files.begin(); it != files.end(); it++)
00472   {
00473     sendPacket.strToBuf((*it).c_str());
00474     sendPacket.uByte4ToBuf(fileToATime[(*it)]);
00475     sendPacket.uByte4ToBuf(fileToMTime[(*it)]);
00476     sendPacket.uByte4ToBuf(fileToSize[(*it)]);
00477     if (printing)
00478     {
00479       time_t atime = fileToATime[(*it)];
00480       time_t mtime = fileToMTime[(*it)];
00481       printf("\tname: %s\n", (*it).c_str());
00482       printf("\t\tlastAccessed: %s", ctime(&atime));
00483       printf("\t\tlastModified: %s", ctime(&mtime));
00484       printf("\t\tsize: %d", fileToSize[(*it)]);
00485     }
00486   }
00487   delete[] dirStr;
00488   if (client != NULL)
00489     client->sendPacketTcp(&sendPacket);
00490 }
00491 
00492 
00493 AREXPORT ArServerFileToClient::ArServerFileToClient(ArServerBase *server, 
00494                                                     const char *topDir) :
00495   myGetFileCB(this, &ArServerFileToClient::getFile)
00496 {
00497   myServer = server;
00498   myServer->addData("getFile", 
00499                     "Gets a file (use ArClientFileToClient instead of calling this directly since this interface may change)",
00500                     &myGetFileCB, "string: file to get; byte2: operation, 0 == get, 1 == cancel (these aren't implemented yet but will be)",
00501                     "ubyte2: return code, 0 = good (sending file), 1 = tried to go outside allowed area, 2 = no such file (or can't read), 3 = empty file name, 4 = error reading file (can happen after some good values) ; string: fileGotten;IF return was 0 then  ubyte4: numBytes (number of bytes in this packet, 0 means end of file; numBytes of data", 
00502                     "FileAccess", "RETURN_UNTIL_EMPTY");
00503 
00504   // snag our base dir and make sure we have enough room for a /
00505   strncpy(myBaseDir, topDir, sizeof(myBaseDir) - 2);
00506   myBaseDir[sizeof(myBaseDir) - 2] = '\0';
00507   // make sure it has a slash
00508   ArUtil::appendSlash(myBaseDir, sizeof(myBaseDir));
00509   // make sure the slashes go the right direction
00510   ArUtil::fixSlashes(myBaseDir, sizeof(myBaseDir));  
00511 }
00512 
00513 AREXPORT ArServerFileToClient::~ArServerFileToClient()
00514 {
00515 
00516 }
00517 
00518 
00519 
00520 AREXPORT void ArServerFileToClient::getFile(ArServerClient *client,
00521                                           ArNetPacket *packet)
00522 {
00523   ArNetPacket sendPacket;
00524   size_t ui;
00525   size_t len;
00526 
00527   char fileNameRaw[2048];
00528   packet->bufToStr(fileNameRaw, sizeof(fileNameRaw));
00529 
00530   // should check for operation here, but thats not implemented yet
00531 
00532   char fileNameCooked[2048];
00533   strcpy(fileNameCooked, fileNameRaw);
00534   ArUtil::fixSlashes(fileNameCooked, sizeof(fileNameCooked));
00535 
00536 
00537   char fileName[2048];
00538   if (!ArServerFileUtil::squashCase(myBaseDir, fileNameCooked, 
00539                                     fileName, sizeof(fileName)))
00540   {
00541     ArLog::log(ArLog::Normal, 
00542                "ArServerFileToClient: can't open file '%s'", fileNameRaw);
00543     sendPacket.uByte2ToBuf(2);
00544     sendPacket.strToBuf(fileNameRaw);
00545     client->sendPacketTcp(&sendPacket);
00546     // send an empty packet so that forwarding knows we're done
00547     sendPacket.empty();
00548     client->sendPacketTcp(&sendPacket);
00549     return;
00550   }
00551 
00552   len = strlen(fileName);
00553   // first advance to the first non space
00554   for (ui = 0; 
00555        ui < len && fileName[ui] != '\0' && isspace(fileName[ui]); 
00556        ui++);
00557 
00558   char *fileStr = new char[len + 3];
00559 
00560   // now copy in the rest
00561   strncpy(fileStr, fileName, len - ui);
00562   // make sure its null terminated
00563   fileStr[len - ui] = '\0';
00564     
00565   if (fileStr[0] == '/' || fileStr[0] == '~' ||
00566       fileStr[0] == '\\' || strstr(fileStr, "..") != NULL)
00567   {
00568     ArLog::log(ArLog::Normal, 
00569                "ArServerFileToClient: '%s' tried to access outside allowed area",
00570                fileStr);
00571     delete[] fileStr;
00572     sendPacket.uByte2ToBuf(1);    
00573     sendPacket.strToBuf(fileNameRaw);
00574     client->sendPacketTcp(&sendPacket);
00575     // send an empty packet so that forwarding knows we're done
00576     sendPacket.empty();
00577     client->sendPacketTcp(&sendPacket);
00578     return;
00579   }
00580 
00581   if (strlen(fileStr) > 0)
00582   {
00583     ArUtil::fixSlashes(fileStr, len + 2);
00584   }
00585   else
00586   {
00587     ArLog::log(ArLog::Normal, 
00588                "ArServerFileToClient: can't get file, empty filename");
00589     delete[] fileStr;
00590     sendPacket.uByte2ToBuf(3);
00591     sendPacket.strToBuf(fileNameRaw);
00592     client->sendPacketTcp(&sendPacket);
00593     // send an empty packet so that forwarding knows we're done
00594     sendPacket.empty();
00595     client->sendPacketTcp(&sendPacket);
00596     return;
00597   }
00598 
00599   // walk from our base down and try to find the first by name
00600   // ignoring case
00601   
00602   // put our base and where we want to go together
00603   std::string wholeName;
00604   wholeName = myBaseDir;
00605   wholeName += fileStr;
00606 
00607   delete[] fileStr;
00608 
00609   ArLog::log(ArLog::Verbose, 
00610              "ArServerFileToClient: Trying to open %s from base %s", 
00611              wholeName.c_str(), myBaseDir);
00612   FILE *file;
00613   if ((file = fopen(wholeName.c_str(), "rb")) == NULL)
00614   {
00615     ArLog::log(ArLog::Normal, 
00616                "ArServerFileToClient: can't open file '%s'", fileName);
00617     sendPacket.uByte2ToBuf(2);
00618     sendPacket.strToBuf(fileNameRaw);
00619     client->sendPacketTcp(&sendPacket);
00620     // send an empty packet so that forwarding knows we're done
00621     sendPacket.empty();
00622     client->sendPacketTcp(&sendPacket);
00623     return;
00624   }
00625 
00626   
00627   char buf[30000];
00628   size_t ret;
00629   // now send the file
00630   while ((ret = fread(buf, 1, sizeof(buf), file)) == sizeof(buf))
00631   {
00632     sendPacket.empty();
00633     sendPacket.uByte2ToBuf(0);
00634     sendPacket.strToBuf(fileNameRaw);
00635     sendPacket.uByte4ToBuf(ret);
00636     sendPacket.dataToBuf(buf, ret);
00637     client->sendPacketTcp(&sendPacket);
00638     //printf("Sent packet with %d\n", ret);
00639   }
00640   if (ferror(file))
00641   {
00642     ArLog::log(ArLog::Normal, "ArServerFileToClient: Error sending file %s", 
00643                fileName);
00644     sendPacket.empty();
00645     sendPacket.uByte2ToBuf(4);
00646     sendPacket.strToBuf(fileNameRaw);
00647     client->sendPacketTcp(&sendPacket);
00648     // send an empty packet so that forwarding knows we're done
00649     sendPacket.empty();
00650     client->sendPacketTcp(&sendPacket);
00651     return;
00652   }
00653 
00654   sendPacket.empty();
00655   sendPacket.uByte2ToBuf(0);
00656   sendPacket.strToBuf(fileNameRaw);
00657   sendPacket.uByte4ToBuf(ret);
00658   sendPacket.dataToBuf(buf, ret);
00659   //printf("Sent packet with %d\n", ret);
00660   client->sendPacketTcp(&sendPacket);
00661   
00662   // if the file itself was empty we just already sent one with a 0
00663   // left so don't send this one since it'd be redundant
00664   if (ret != 0)
00665   {
00666     sendPacket.empty();
00667     sendPacket.uByte2ToBuf(0);
00668     sendPacket.strToBuf(fileNameRaw);
00669     sendPacket.uByte4ToBuf(0);
00670     //printf("Sent packet end\n");
00671     client->sendPacketTcp(&sendPacket);
00672   }
00673   // send an empty packet so that forwarding knows we're done
00674   sendPacket.empty();
00675   client->sendPacketTcp(&sendPacket);
00676 
00677   fclose(file);
00678   if (feof(file))
00679   {
00680     ArLog::log(ArLog::Normal, "ArServerFileToClient: Sent file %s to %s", 
00681                fileName, client->getIPString());
00682   }
00683 }
00684 
00685 AREXPORT ArServerFileFromClient::ArServerFileFromClient(ArServerBase *server, 
00686                                                         const char *topDir,
00687                                                         const char *tempDir) :
00688   myPutFileCB(this, &ArServerFileFromClient::putFile),
00689   myPutFileInterleavedCB(this, &ArServerFileFromClient::putFileInterleaved)
00690 {
00691   myServer = server;
00692   myServer->addData("putFile", 
00693                     "Puts a file (use ArClientFileFromClient instead of calling this directly since this interface may change)",
00694                     &myPutFileCB, "uByte2: command, 0 = start file, 1 = continue file, 2 done with file, 3 = cancel put; string: file being sent; IF command == 1 then uByte4: numBytes; numBytes of data",
00695                     "uByte2: return code, 0 = good (got file), 1 = getting file, 2 = tried to go outside allowed area, 3 = bad directory, 4 = empty file name (or other problem with fileName), 5 = can't write temp file, 6 = error moving file from temp to perm, 7 = another client putting file, 8 = timeout (no activity for 15 seconds) and another client wanted to put the file, 9 = client adding to, finishing, or canceling a file the server doesn't have;  string: fileName",
00696                     "FileAccess", "RETURN_COMPLEX");
00697 
00698   myServer->addData("putFileInterleaved", 
00699                     "Puts a file (use ArClientFileFromClient instead of calling this directly since this interface may change)",
00700                     &myPutFileInterleavedCB, "uByte2: command, 0 = start file, 1 = continue file, 2 done with file, 3 = cancel put; string: file being sent; IF command == 1 then uByte4: numBytes; numBytes of data",
00701                     "uByte2: return code, 0 = good (got file), 1 = getting file, 2 = tried to go outside allowed area, 3 = bad directory, 4 = empty file name (or other problem with fileName), 5 = can't write temp file, 6 = error moving file from temp to perm, 7 = another client putting file, 8 = timeout (no activity for 15 seconds) and another client wanted to put the file, 9 = client adding to, finishing, or canceling a file the server doesn't have, 10 = gotPacket, awaiting next packet, 11 = cancelled put; string: fileName",
00702                     "FileAccess", "RETURN_SINGLE");
00703 
00704   myFileNumber = 0;
00705   // snag our base dir and make sure we have enough room for a /
00706   strncpy(myBaseDir, topDir, sizeof(myBaseDir) - 2);
00707   myBaseDir[sizeof(myBaseDir) - 2] = '\0';
00708   // make sure it has a slash
00709   ArUtil::appendSlash(myBaseDir, sizeof(myBaseDir));
00710   // make sure the slashes go the right direction
00711   ArUtil::fixSlashes(myBaseDir, sizeof(myBaseDir));  
00712   // snag our base dir and make sure we have enough room for a /
00713   strncpy(myTempDir, tempDir, sizeof(myTempDir) - 2);
00714   myTempDir[sizeof(myTempDir) - 2] = '\0';
00715   // make sure it has a slash
00716   ArUtil::appendSlash(myTempDir, sizeof(myTempDir));
00717   // make sure the slashes go the right direction
00718   ArUtil::fixSlashes(myTempDir, sizeof(myTempDir));  
00719 }
00720 
00721 AREXPORT ArServerFileFromClient::~ArServerFileFromClient()
00722 {
00723 
00724 }
00725 
00726 
00727 AREXPORT void ArServerFileFromClient::putFile(ArServerClient *client,
00728                                               ArNetPacket *packet)
00729 {
00730   internalPutFile(client, packet, false);
00731 }
00732 
00733 AREXPORT void ArServerFileFromClient::putFileInterleaved(
00734         ArServerClient *client, ArNetPacket *packet)
00735 {
00736   internalPutFile(client, packet, true);
00737 }
00738 
00739 AREXPORT void ArServerFileFromClient::internalPutFile(ArServerClient *client, 
00740                                                       ArNetPacket *packet,
00741                                                       bool interleaved)
00742 {
00743   ArNetPacket sendPacket;
00744   std::list<ArFunctor *>::iterator fit;
00745   std::map<std::string, FileInfo *>::iterator it;
00746   FileInfo *info;
00747   std::string fileName;
00748   char fileNameRaw[2048];
00749   fileNameRaw[0] = '\0';
00750   ArTypes::UByte2 doing;
00751   doing = packet->bufToUByte2();
00752   packet->bufToStr(fileNameRaw, sizeof(fileNameRaw));
00753   if (doing == 0)
00754   {
00755     char directoryRaw[2048];
00756     directoryRaw[0] = '\0';
00757     char fileNamePart[2048];
00758     fileNamePart[0] = '\0';
00759     if (!ArServerFileUtil::getDirectory(fileNameRaw, 
00760                                         directoryRaw, sizeof(directoryRaw)) ||
00761         !ArServerFileUtil::getFileName(fileNameRaw, 
00762                                        fileNamePart, sizeof(fileNamePart)))
00763     {
00764       ArLog::log(ArLog::Normal, 
00765                  "ArServerFileFromClient: Problem with filename '%s'", 
00766                  fileNameRaw);
00767       sendPacket.uByte2ToBuf(4);
00768       sendPacket.strToBuf(fileNameRaw);
00769       client->sendPacketTcp(&sendPacket);
00770       // just added this 4/5/06
00771       return;
00772     }
00773     
00774     char directory[2048];
00775     printf("DirectoryRaw %s\n", directoryRaw);
00776     if (strlen(directoryRaw) == 0)
00777     {
00778       strcpy(directory, ".");
00779     }
00780     else if (!ArServerFileUtil::squashCase(myBaseDir, directoryRaw, directory, 
00781                                         sizeof(directory)))
00782       {
00783         ArLog::log(ArLog::Normal, 
00784                    "ArServerFileFromClient: Bad directory for '%s'", 
00785                  fileNameRaw);
00786       sendPacket.uByte2ToBuf(3);
00787       sendPacket.strToBuf(fileNameRaw);
00788       client->sendPacketTcp(&sendPacket);
00789       return;
00790     }
00791 
00792     char tmpDir[2048];
00793     tmpDir[0] = '\0';
00794     //sprintf(tmpDir, "%s", tmpDir, directory);
00795     strcpy(tmpDir, directory);
00796     ArUtil::appendSlash(tmpDir, sizeof(tmpDir));
00797     char squashedFileName[2048];
00798     
00799     if (ArServerFileUtil::squashCase(tmpDir, fileNamePart, 
00800                                      squashedFileName, 
00801                                      sizeof(squashedFileName)))
00802     {
00803       fileName = tmpDir;
00804       fileName += squashedFileName;
00805       //printf("squashed from %s %s\n", tmpDir, squashedFileName);
00806     }
00807     else
00808     {
00809       fileName = tmpDir;
00810       fileName += fileNamePart;
00811       //printf("unsquashed from %s %s\n", tmpDir, fileNamePart);
00812     }
00813     
00814     ArLog::log(ArLog::Normal, 
00815                "ArServerFileFromClient: Checking file %s (as %s)",
00816                fileNameRaw, fileName.c_str());
00817     for (it = myMap.begin(); it != myMap.end(); it++)
00818     {
00819       info = (*it).second;
00820       // see if a client is messing with this file
00821       if (ArUtil::strcasecmp(info->myRealFileName, fileName) == 0)
00822       {
00823         // see if the other one messing with this timed out, if so let 'em know
00824         if (info->myLastActivity.secSince() > 15)
00825         {
00826           ArLog::log(ArLog::Normal, 
00827                      "ArServerFileFromClient: Another client wants to start putting file '%s' and old client was inactive", 
00828                      (*it).first.c_str());
00829           if (!interleaved)
00830           {
00831             sendPacket.uByte2ToBuf(8);
00832             sendPacket.strToBuf((*it).first.c_str());
00833             // NOT_EXCLUDING this isn't excluding anymore because it
00834             // used to cause problems...  this may cause some
00835             // problems, but hopefully the event doesn't happen much,
00836             // and at least this way things'll be able to retry easier
00837             myServer->broadcastPacketTcp(&sendPacket, "putFile");//, client);
00838           }
00839           myMap.erase((*it).first);
00840           delete info;
00841           break;
00842         }
00843         // otherwise let this guy know its busy
00844         else
00845         {
00846           ArLog::log(ArLog::Normal, 
00847                      "ArServerFileFromClient: Another client putting file '%s'", 
00848                      fileNameRaw);
00849           sendPacket.uByte2ToBuf(7);
00850           sendPacket.strToBuf(fileNameRaw);
00851           client->sendPacketTcp(&sendPacket);
00852           return;
00853         }
00854       }
00855     }
00856     ArLog::log(ArLog::Normal, 
00857                "ArServerFileFromClient: Receiving file %s (as %s)", 
00858                fileNameRaw, fileName.c_str());
00859     char tempFileName[3200];
00860     tempFileName[0] = '\0';
00861     sprintf(tempFileName, "%sArServerFileFromClient.%d.%d", myTempDir, getpid(), myFileNumber);
00862     myFileNumber++;
00863 
00864     printf("Using temp file name %s\n", tempFileName);
00865     // see if we can open the temp file
00866     FILE *file;
00867     if ((file = fopen(tempFileName, "wb")) != NULL)
00868     {
00869       info = new FileInfo;
00870       info->myRealFileName = fileName;      
00871       info->myTempFileName = tempFileName;
00872       info->myFile = file;
00873       info->myStartedTransfer.setToNow();
00874       info->myLastActivity.setToNow();
00875       myMap[fileNameRaw] = info;
00876       if (interleaved)
00877       {
00878         //printf("Sending continue\n");
00879         sendPacket.uByte2ToBuf(10);
00880         sendPacket.strToBuf(fileNameRaw);
00881         client->sendPacketTcp(&sendPacket);
00882       }
00883     }
00884     else
00885     {
00886       ArLog::log(ArLog::Normal, 
00887                  "ArServerFileFromClient: Can't open tmp file for '%s' (tried '%s')", 
00888                  fileNameRaw, tempFileName);
00889       sendPacket.uByte2ToBuf(5);
00890       sendPacket.strToBuf(fileNameRaw);
00891       client->sendPacketTcp(&sendPacket);
00892       return;
00893     }
00894   }
00895   else if (doing == 1)
00896   {
00897     // see if we can find this file
00898     if ((it = myMap.find(fileNameRaw)) == myMap.end())
00899     {
00900       ArLog::log(ArLog::Normal, "ArServerFileUtil: Couldn't find entry for '%s'", fileNameRaw);
00901       sendPacket.uByte2ToBuf(9);
00902       sendPacket.strToBuf(fileNameRaw);
00903       client->sendPacketTcp(&sendPacket);
00904       return;
00905     }
00906     info = (*it).second;
00907     // write the data to the file and increment last activity
00908     ArTypes::UByte4 numBytes;
00909     char buf[32000];
00910     numBytes = packet->bufToUByte4();
00911     packet->bufToData(buf, numBytes);
00912     fwrite(buf, 1, numBytes, info->myFile);
00913     info->myLastActivity.setToNow();
00914     if (interleaved)
00915     {
00916       sendPacket.uByte2ToBuf(10);
00917       sendPacket.strToBuf(fileNameRaw);
00918       client->sendPacketTcp(&sendPacket);
00919     }
00920     ArLog::log(ArLog::Verbose, "Continuing put file %s (%d bytes)", 
00921                fileNameRaw, numBytes);
00922     
00923   }
00924   else if (doing == 2)
00925   {
00926     // see if we can find this file
00927     if ((it = myMap.find(fileNameRaw)) == myMap.end())
00928     {
00929       ArLog::log(ArLog::Normal, "ArServerFileUtil: Couldn't find entry for '%s'", fileNameRaw);
00930       sendPacket.uByte2ToBuf(9);
00931       sendPacket.strToBuf(fileNameRaw);
00932       client->sendPacketTcp(&sendPacket);
00933       return;
00934     }
00935     info = (*it).second;
00936     fclose(info->myFile);
00937     info->myFile = NULL;
00938 
00939     char systemBuf[6400];
00940 #ifndef WIN32
00941     char *mvName = "mv";
00942 #else
00943     char *mvName = "move";
00944 #endif
00945     sprintf(systemBuf, "%s \"%s\" \"%s\"", mvName, info->myTempFileName.c_str(), 
00946             info->myRealFileName.c_str());
00947     
00948 
00949     myMovingFileName = info->myRealFileName;
00950 
00951     // call our pre move callbacks
00952     for (fit = myPreMoveCallbacks.begin(); 
00953          fit != myPreMoveCallbacks.end(); 
00954          fit++)
00955       (*fit)->invoke();
00956 
00957     // move file
00958     int ret;
00959     if ((ret = system(systemBuf)) == 0)
00960     {
00961       sendPacket.uByte2ToBuf(0);
00962       sendPacket.strToBuf(fileNameRaw);
00963       client->sendPacketTcp(&sendPacket);
00964       ArLog::log(ArLog::Normal, "Done with file %s from %s", fileNameRaw,
00965                  client->getIPString());
00966       myMap.erase((*it).first);
00967       delete info;
00968     }
00969     else
00970     {
00971       sendPacket.uByte2ToBuf(6);
00972       sendPacket.strToBuf(fileNameRaw);
00973       client->sendPacketTcp(&sendPacket);
00974       unlink(info->myTempFileName.c_str());
00975       myMap.erase((*it).first);
00976       delete info;
00977       ArLog::log(ArLog::Normal, "Couldn't move temp file for %s (ret of '%s' is %d)", fileNameRaw, systemBuf, ret);
00978     }
00979 
00980     // call our post move callbacks
00981     for (fit = myPostMoveCallbacks.begin(); 
00982          fit != myPostMoveCallbacks.end(); 
00983          fit++)
00984       (*fit)->invoke();
00985 
00986     myMovingFileName = "";
00987   }
00988   else if (doing == 3)
00989   {
00990     if ((it = myMap.find(fileNameRaw)) == myMap.end())
00991     {
00992       ArLog::log(ArLog::Normal, "ArServerFileUtil: Couldn't find entry to cancel for '%s'", fileNameRaw);
00993       sendPacket.uByte2ToBuf(9);
00994       sendPacket.strToBuf(fileNameRaw);
00995       client->sendPacketTcp(&sendPacket);
00996       return;
00997     }
00998     info = (*it).second;
00999     fclose(info->myFile);
01000     info->myFile = NULL;
01001 
01002     unlink(info->myTempFileName.c_str());
01003     myMap.erase((*it).first);
01004     delete info;
01005 
01006     ArLog::log(ArLog::Normal, "Cancelling file %s", fileNameRaw);
01007     if (interleaved)
01008     {
01009       sendPacket.uByte2ToBuf(11);
01010       sendPacket.strToBuf(fileNameRaw);
01011       client->sendPacketTcp(&sendPacket);
01012     }
01013   }
01014   else
01015   {
01016     ArLog::log(ArLog::Normal, "Unknown command %d for file %s", doing, 
01017                fileNameRaw);
01018   }
01019 }
01020 
01021 AREXPORT void ArServerFileFromClient::addPreMoveCallback(
01022         ArFunctor *functor, ArListPos::Pos position)
01023 {
01024   if (position == ArListPos::FIRST)
01025     myPreMoveCallbacks.push_front(functor);
01026   else if (position == ArListPos::LAST)
01027     myPreMoveCallbacks.push_back(functor);
01028   else
01029     ArLog::log(ArLog::Terse, 
01030        "ArServerFileFromClient::addPreMoveCallback: Invalid position.");
01031 }
01032 
01033 AREXPORT void ArServerFileFromClient::remPreMoveCallback(
01034         ArFunctor *functor)
01035 {
01036   myPreMoveCallbacks.remove(functor);
01037 }
01038 
01039 AREXPORT void ArServerFileFromClient::addPostMoveCallback(
01040         ArFunctor *functor, ArListPos::Pos position)
01041 {
01042   if (position == ArListPos::FIRST)
01043     myPostMoveCallbacks.push_front(functor);
01044   else if (position == ArListPos::LAST)
01045     myPostMoveCallbacks.push_back(functor);
01046   else
01047     ArLog::log(ArLog::Terse, 
01048        "ArServerFileFromClient::addPostMoveCallback: Invalid position.");
01049 }
01050 
01051 AREXPORT void ArServerFileFromClient::remPostMoveCallback(
01052         ArFunctor *functor)
01053 {
01054   myPostMoveCallbacks.remove(functor);
01055 }
01056 
01057 
01058 AREXPORT ArServerDeleteFileOnServer::ArServerDeleteFileOnServer(
01059         ArServerBase *server, const char *topDir) :
01060   myDeleteFileCB(this, &ArServerDeleteFileOnServer::deleteFile)
01061 {
01062   myServer = server;
01063   myServer->addData("deleteFile", 
01064                     "Deletes a file (use ArClientDeleteFileOnServer instead of calling this directly since this interface may change)",
01065                     &myDeleteFileCB, "string: file to delete",
01066                     "ubyte2: return code, 0 = good (deleted file file), 1 = tried to go outside allowed area, 2 = no such file (or can't read it), 3 = empty file name, string: fileDeleted", 
01067                     "FileAccess", "RETURN_SINGLE");
01068 
01069   // snag our base dir and make sure we have enough room for a /
01070   strncpy(myBaseDir, topDir, sizeof(myBaseDir) - 2);
01071   myBaseDir[sizeof(myBaseDir) - 2] = '\0';
01072   // make sure it has a slash
01073   ArUtil::appendSlash(myBaseDir, sizeof(myBaseDir));
01074   // make sure the slashes go the right direction
01075   ArUtil::fixSlashes(myBaseDir, sizeof(myBaseDir));  
01076 }
01077 
01078 AREXPORT ArServerDeleteFileOnServer::~ArServerDeleteFileOnServer()
01079 {
01080 
01081 }
01082 
01083 
01084 
01085 AREXPORT void ArServerDeleteFileOnServer::deleteFile(ArServerClient *client,
01086                                           ArNetPacket *packet)
01087 {
01088   ArNetPacket sendPacket;
01089   size_t ui;
01090   size_t len;
01091   std::list<ArFunctor *>::iterator fit;
01092   char fileNameRaw[2048];
01093   packet->bufToStr(fileNameRaw, sizeof(fileNameRaw));
01094 
01095   // should check for operation here, but thats not implemented yet
01096 
01097   char fileNameCooked[2048];
01098   strcpy(fileNameCooked, fileNameRaw);
01099   ArUtil::fixSlashes(fileNameCooked, sizeof(fileNameCooked));
01100 
01101 
01102   char fileName[2048];
01103   if (!ArServerFileUtil::squashCase(myBaseDir, fileNameCooked, 
01104                                     fileName, sizeof(fileName)))
01105   {
01106     ArLog::log(ArLog::Normal, 
01107                "ArServerDeleteFileOnServer: can't read file '%s'", 
01108                fileNameRaw);
01109     sendPacket.uByte2ToBuf(2);
01110     sendPacket.strToBuf(fileNameRaw);
01111     client->sendPacketTcp(&sendPacket);
01112     return;
01113   }
01114 
01115   len = strlen(fileName);
01116   // first advance to the first non space
01117   for (ui = 0; 
01118        ui < len && fileName[ui] != '\0' && isspace(fileName[ui]); 
01119        ui++);
01120 
01121   char *fileStr = new char[len + 3];
01122 
01123   // now copy in the rest
01124   strncpy(fileStr, fileName, len - ui);
01125   // make sure its null terminated
01126   fileStr[len - ui] = '\0';
01127     
01128   if (fileStr[0] == '/' || fileStr[0] == '~' ||
01129       fileStr[0] == '\\' || strstr(fileStr, "..") != NULL)
01130   {
01131     ArLog::log(ArLog::Normal, 
01132                "ArServerDeleteFileOnServer: '%s' tried to access outside allowed area",
01133                fileStr);
01134     delete[] fileStr;
01135     sendPacket.uByte2ToBuf(1);    
01136     sendPacket.strToBuf(fileNameRaw);
01137     client->sendPacketTcp(&sendPacket);
01138     return;
01139   }
01140 
01141   if (strlen(fileStr) > 0)
01142   {
01143     ArUtil::fixSlashes(fileStr, len + 2);
01144   }
01145   else
01146   {
01147     ArLog::log(ArLog::Normal, 
01148        "ArServerDeleteFileOnServer: can't delete file, empty filename");
01149     delete[] fileStr;
01150     sendPacket.uByte2ToBuf(3);
01151     sendPacket.strToBuf(fileNameRaw);
01152     client->sendPacketTcp(&sendPacket);
01153     return;
01154   }
01155 
01156   // walk from our base down and try to find the first by name
01157   // ignoring case
01158   
01159   // put our base and where we want to go together
01160   std::string wholeName;
01161   wholeName = myBaseDir;
01162   wholeName += fileStr;
01163 
01164   delete[] fileStr;
01165 
01166   ArLog::log(ArLog::Verbose, 
01167              "ArServerDeleteFileOnServer: Trying to delete %s from base %s", 
01168              wholeName.c_str(), myBaseDir);
01169 
01170   myDeletingFileName = fileNameRaw;
01171 
01172   // call our pre delete callbacks
01173   for (fit = myPreDeleteCallbacks.begin(); 
01174        fit != myPreDeleteCallbacks.end(); 
01175        fit++)
01176     (*fit)->invoke();
01177 
01178   if (unlink(wholeName.c_str()) == 0)
01179   {
01180     ArLog::log(ArLog::Normal, 
01181                "ArServerDeleteFileOnServer: Deleted file %s for %s", 
01182                fileName, client->getIPString());
01183     sendPacket.uByte2ToBuf(0);
01184     sendPacket.strToBuf(fileNameRaw);
01185     client->sendPacketTcp(&sendPacket);
01186   }
01187   else
01188   {
01189     ArLog::log(ArLog::Normal, 
01190                "ArServerDeleteFileOnServer: can't unlink file '%s'", fileName);
01191     sendPacket.uByte2ToBuf(2);
01192     sendPacket.strToBuf(fileNameRaw);
01193     client->sendPacketTcp(&sendPacket);
01194   }
01195 
01196   // call our post delete callbacks
01197   for (fit = myPostDeleteCallbacks.begin(); 
01198        fit != myPostDeleteCallbacks.end(); 
01199        fit++)
01200     (*fit)->invoke();
01201 
01202   myDeletingFileName = "";  
01203 }
01204 
01205 AREXPORT void ArServerDeleteFileOnServer::addPreDeleteCallback(
01206         ArFunctor *functor, ArListPos::Pos position)
01207 {
01208   if (position == ArListPos::FIRST)
01209     myPreDeleteCallbacks.push_front(functor);
01210   else if (position == ArListPos::LAST)
01211     myPreDeleteCallbacks.push_back(functor);
01212   else
01213     ArLog::log(ArLog::Terse, 
01214        "ArServerDeleteFileOnServer::addPreDeleteCallback: Invalid position.");
01215 }
01216 
01217 AREXPORT void ArServerDeleteFileOnServer::remPreDeleteCallback(
01218         ArFunctor *functor)
01219 {
01220   myPreDeleteCallbacks.remove(functor);
01221 }
01222 
01223 AREXPORT void ArServerDeleteFileOnServer::addPostDeleteCallback(
01224         ArFunctor *functor, ArListPos::Pos position)
01225 {
01226   if (position == ArListPos::FIRST)
01227     myPostDeleteCallbacks.push_front(functor);
01228   else if (position == ArListPos::LAST)
01229     myPostDeleteCallbacks.push_back(functor);
01230   else
01231     ArLog::log(ArLog::Terse, 
01232        "ArServerDeleteFileOnServer::addPostDeleteCallback: Invalid position.");
01233 }
01234 
01235 AREXPORT void ArServerDeleteFileOnServer::remPostDeleteCallback(
01236         ArFunctor *functor)
01237 {
01238   myPostDeleteCallbacks.remove(functor);
01239 }
01240 
01241 #endif // WIN32

Generated on Tue Feb 20 10:51:50 2007 for ArNetworking by  doxygen 1.4.0