ftp_directory_listing_parser_vms.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_directory_listing_parser_vms.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_util.h" 12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_directory_listing_parser.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_util.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Converts the filename component in listing to the filename we can display. 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true on success. 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool ParseVmsFilename(const base::string16& raw_filename, 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16* parsed_filename, 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FtpDirectoryListingEntry::Type* type) { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // On VMS, the files and directories are versioned. The version number is 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // separated from the file name by a semicolon. Example: ANNOUNCE.TXT;2. 28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> listing_parts; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(raw_filename, ';', &listing_parts); 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (listing_parts.size() != 2) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int version_number; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(listing_parts[1], &version_number)) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (version_number < 0) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Even directories have extensions in the listings. Don't display extensions 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // for directories; it's awkward for non-VMS users. Also, VMS is 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // case-insensitive, but generally uses uppercase characters. This may look 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // awkward, so we convert them to lower case. 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> filename_parts; 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(listing_parts[0], '.', &filename_parts); 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (filename_parts.size() != 2) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (EqualsASCII(filename_parts[1], "DIR")) { 476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *parsed_filename = base::StringToLowerASCII(filename_parts[0]); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *type = FtpDirectoryListingEntry::DIRECTORY; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *parsed_filename = base::StringToLowerASCII(listing_parts[0]); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *type = FtpDirectoryListingEntry::FILE; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool ParseVmsFilesize(const base::string16& input, int64* size) { 57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (base::ContainsOnlyChars(input, base::ASCIIToUTF16("*"))) { 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Response consisting of asterisks means unknown size. 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *size = -1; 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // VMS's directory listing gives us file size in blocks. We assume that 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the block size is 512 bytes. It doesn't give accurate file size, but is the 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // best information we have. 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int kBlockSize = 512; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base::StringToInt64(input, size)) { 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (*size < 0) 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *size *= kBlockSize; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> parts; 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(input, '/', &parts); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parts.size() != 2) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int64 blocks_used, blocks_allocated; 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt64(parts[0], &blocks_used)) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt64(parts[1], &blocks_allocated)) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (blocks_used > blocks_allocated) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (blocks_used < 0 || blocks_allocated < 0) 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *size = blocks_used * kBlockSize; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool LooksLikeVmsFileProtectionListingPart(const base::string16& input) { 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (input.length() > 4) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // On VMS there are four different permission bits: Read, Write, Execute, 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and Delete. They appear in that order in the permission listing. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string pattern("RWED"); 101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 match(input); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (!match.empty() && !pattern.empty()) { 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (match[0] == pattern[0]) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match = match.substr(1); 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pattern = pattern.substr(1); 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return match.empty(); 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool LooksLikeVmsFileProtectionListing(const base::string16& input) { 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (input.length() < 2) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (input[0] != '(' || input[input.length() - 1] != ')') 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We expect four parts of the file protection listing: for System, Owner, 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Group, and World. 118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> parts; 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(input.substr(1, input.length() - 2), ',', &parts); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parts.size() != 4) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return LooksLikeVmsFileProtectionListingPart(parts[0]) && 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LooksLikeVmsFileProtectionListingPart(parts[1]) && 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LooksLikeVmsFileProtectionListingPart(parts[2]) && 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LooksLikeVmsFileProtectionListingPart(parts[3]); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool LooksLikeVmsUserIdentificationCode(const base::string16& input) { 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (input.length() < 2) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return input[0] == '[' && input[input.length() - 1] == ']'; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool LooksLikeVMSError(const base::string16& text) { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const char* kPermissionDeniedMessages[] = { 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "%RMS-E-FNF", // File not found. 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "%RMS-E-PRV", // Access denied. 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "%SYSTEM-F-NOPRIV", 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "privilege", 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < arraysize(kPermissionDeniedMessages); i++) { 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (text.find(base::ASCIIToUTF16(kPermissionDeniedMessages[i])) != 145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16::npos) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool VmsDateListingToTime(const std::vector<base::string16>& columns, 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time* time) { 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(4U, columns.size()); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::Exploded time_exploded = { 0 }; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Date should be in format DD-MMM-YYYY. 159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> date_parts; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(columns[2], '-', &date_parts); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (date_parts.size() != 3) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(date_parts[0], &time_exploded.day_of_month)) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!FtpUtil::AbbreviatedMonthToNumber(date_parts[1], 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &time_exploded.month)) 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(date_parts[2], &time_exploded.year)) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Time can be in format HH:MM, HH:MM:SS, or HH:MM:SS.mm. Try to recognize the 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // last type first. Do not parse the seconds, they will be ignored anyway. 173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 time_column(columns[3]); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_column.length() == 11 && time_column[8] == '.') 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_column = time_column.substr(0, 8); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_column.length() == 8 && time_column[5] == ':') 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_column = time_column.substr(0, 5); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_column.length() != 5) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> time_parts; 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(time_column, ':', &time_parts); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_parts.size() != 2) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(time_parts[0], &time_exploded.hour)) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(time_parts[1], &time_exploded.minute)) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We don't know the time zone of the server, so just use local time. 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *time = base::Time::FromLocalExploded(time_exploded); 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ParseFtpDirectoryListingVms( 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::vector<base::string16>& lines, 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<FtpDirectoryListingEntry>* entries) { 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The first non-empty line is the listing header. It often 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // starts with "Directory ", but not always. We set a flag after 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // seing the header. 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool seen_header = false; 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Sometimes the listing doesn't end with a "Total" line, but 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // it's only okay when it contains some errors (it's needed 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // to distinguish it from "ls -l" format). 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool seen_error = false; 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < lines.size(); i++) { 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (lines[i].empty()) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (StartsWith(lines[i], base::ASCIIToUTF16("Total of "), true)) { 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // After the "total" line, all following lines must be empty. 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t j = i + 1; j < lines.size(); j++) 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!lines[j].empty()) 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!seen_header) { 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) seen_header = true; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (LooksLikeVMSError(lines[i])) { 2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) seen_error = true; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> columns; 233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::SplitString(base::CollapseWhitespace(lines[i], false), ' ', &columns); 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (columns.size() == 1) { 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // There can be no continuation if the current line is the last one. 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (i == lines.size() - 1) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Skip the next line. 2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) i++; 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // This refers to the continuation line. 2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (LooksLikeVMSError(lines[i])) { 2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) seen_error = true; 2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Join the current and next line and split them into columns. 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString( 251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::CollapseWhitespace( 252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) lines[i - 1] + base::ASCIIToUTF16(" ") + lines[i], false), 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ' ', 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &columns); 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FtpDirectoryListingEntry entry; 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ParseVmsFilename(columns[0], &entry.name, &entry.type)) 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // There are different variants of a VMS listing. Some display 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the protection listing and user identification code, some do not. 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (columns.size() == 6) { 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!LooksLikeVmsFileProtectionListing(columns[5])) 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!LooksLikeVmsUserIdentificationCode(columns[4])) 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Drop the unneeded data, so that the following code can always expect 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // just four columns. 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) columns.resize(4); 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (columns.size() != 4) 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ParseVmsFilesize(columns[1], &entry.size)) 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (entry.type != FtpDirectoryListingEntry::FILE) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entry.size = -1; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!VmsDateListingToTime(columns, &entry.last_modified)) 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entries->push_back(entry); 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The only place where we return true is after receiving the "Total" line, 2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // that should be present in every VMS listing. Alternatively, if the listing 2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // contains error messages, it's OK not to have the "Total" line. 2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return seen_error; 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace net 294