|  | @@ -13,13 +13,15 @@
 | 
		
	
		
			
			| 13 | 13 |  #include "CommandLineParser.h"
 | 
		
	
		
			
			| 14 | 14 |  #include "MainClass.h"
 | 
		
	
		
			
			| 15 | 15 |  
 | 
		
	
		
			
			| 16 |  | -#define EX_REDIRECT_ERROR 1
 | 
		
	
		
			
			|  | 16 | +#define EX_OUTPUT_ERROR 1
 | 
		
	
		
			
			|  | 17 | +#define EX_INPUT_ERROR 2
 | 
		
	
		
			
			| 17 | 18 |  #define EX_KEY_ERROR 10
 | 
		
	
		
			
			| 18 | 19 |  #define EX_LIB_NFC_ERROR 12
 | 
		
	
		
			
			| 19 | 20 |  #define EX_NFC_DEVICE_NOT_FOUND 13
 | 
		
	
		
			
			| 20 | 21 |  #define EX_NFC_TAG_NOT_FOUND 14
 | 
		
	
		
			
			| 21 | 22 |  #define EX_MAP_KEYS_ERROR 15
 | 
		
	
		
			
			| 22 |  | -#define EX_DUMP_ERROR 16
 | 
		
	
		
			
			|  | 23 | +#define EX_READ_ERROR 16
 | 
		
	
		
			
			|  | 24 | +#define EX_WRITE_ERROR 17
 | 
		
	
		
			
			| 23 | 25 |  
 | 
		
	
		
			
			| 24 | 26 |  MainClass::MainClass(int argc, char *argv[])
 | 
		
	
		
			
			| 25 | 27 |      : _argc(argc)
 | 
		
	
	
		
			
			|  | @@ -34,7 +36,8 @@ int MainClass::main()
 | 
		
	
		
			
			| 34 | 36 |      CommandLineOption optionHelp(&parser, "help", 'h', "Show this help");
 | 
		
	
		
			
			| 35 | 37 |  
 | 
		
	
		
			
			| 36 | 38 |      CommandLineOption optionMap(&parser, "map", 'm', "Map keys for the tag");
 | 
		
	
		
			
			| 37 |  | -    CommandLineOption optionDump(&parser, "read", 'r', "Read the tag");
 | 
		
	
		
			
			|  | 39 | +    CommandLineOption optionRead(&parser, "read", 'r', "Read the tag");
 | 
		
	
		
			
			|  | 40 | +    CommandLineOption optionWrite(&parser, "write", 'w', "Write the tag");
 | 
		
	
		
			
			| 38 | 41 |      CommandLineOption optionDevices(&parser, "devices", 'd', "List NFC devices");
 | 
		
	
		
			
			| 39 | 42 |      CommandLineOption optionTags(&parser, "tags", 't', "List NFC tags");
 | 
		
	
		
			
			| 40 | 43 |  
 | 
		
	
	
		
			
			|  | @@ -45,6 +48,7 @@ int MainClass::main()
 | 
		
	
		
			
			| 45 | 48 |      CommandLineOption optionKey(&parser, "key", 'k', "Key to use to authenticate", "KEY");
 | 
		
	
		
			
			| 46 | 49 |  
 | 
		
	
		
			
			| 47 | 50 |      CommandLineOption optionOutput(&parser, "output", 'o', "Redirect output to FILE. '-' to use stdout", "FILE", "-");
 | 
		
	
		
			
			|  | 51 | +    CommandLineOption optionInput(&parser, "input", 'i', "Read input from FILE. '-' to use stdin", "FILE", "-");
 | 
		
	
		
			
			| 48 | 52 |  
 | 
		
	
		
			
			| 49 | 53 |      if (!parser.parse()) {
 | 
		
	
		
			
			| 50 | 54 |          return parser.showHelp(EX_USAGE);
 | 
		
	
	
		
			
			|  | @@ -54,17 +58,30 @@ int MainClass::main()
 | 
		
	
		
			
			| 54 | 58 |          outputFile = optionOutput.getValue();
 | 
		
	
		
			
			| 55 | 59 |      }
 | 
		
	
		
			
			| 56 | 60 |  
 | 
		
	
		
			
			| 57 |  | -    std::shared_ptr<std::ofstream> fileCout = 0;
 | 
		
	
		
			
			| 58 | 61 |      if (outputFile != "-" && !outputFile.empty()) {
 | 
		
	
		
			
			| 59 |  | -        fileCout = std::make_shared<std::ofstream>();
 | 
		
	
		
			
			|  | 62 | +        std::shared_ptr<std::ofstream> fileCout = std::make_shared<std::ofstream>();
 | 
		
	
		
			
			| 60 | 63 |          fileCout->open(outputFile);
 | 
		
	
		
			
			| 61 | 64 |          if (!*fileCout) {
 | 
		
	
		
			
			| 62 | 65 |              std::cerr << "Failed to redirect output: " << strerror(errno) << std::endl;
 | 
		
	
		
			
			| 63 |  | -            return EX_REDIRECT_ERROR;
 | 
		
	
		
			
			|  | 66 | +            return EX_OUTPUT_ERROR;
 | 
		
	
		
			
			| 64 | 67 |          }
 | 
		
	
		
			
			| 65 | 68 |          _outputStream = fileCout;
 | 
		
	
		
			
			| 66 | 69 |      }
 | 
		
	
		
			
			| 67 | 70 |  
 | 
		
	
		
			
			|  | 71 | +    std::string inputFile = optionInput.getDefaultValue();
 | 
		
	
		
			
			|  | 72 | +    if (optionInput.isSet()) {
 | 
		
	
		
			
			|  | 73 | +        inputFile = optionInput.getValue();
 | 
		
	
		
			
			|  | 74 | +    }
 | 
		
	
		
			
			|  | 75 | +    if (inputFile != "-" && !inputFile.empty()) {
 | 
		
	
		
			
			|  | 76 | +        std::shared_ptr<std::ifstream> fileCin = std::make_shared<std::ifstream>();
 | 
		
	
		
			
			|  | 77 | +        fileCin->open(inputFile);
 | 
		
	
		
			
			|  | 78 | +        if (!*fileCin) {
 | 
		
	
		
			
			|  | 79 | +            std::cerr << "Failed to open input file: " << strerror(errno) << std::endl;
 | 
		
	
		
			
			|  | 80 | +            return EX_INPUT_ERROR;
 | 
		
	
		
			
			|  | 81 | +        }
 | 
		
	
		
			
			|  | 82 | +        _inputStream = fileCin;
 | 
		
	
		
			
			|  | 83 | +    }
 | 
		
	
		
			
			|  | 84 | +
 | 
		
	
		
			
			| 68 | 85 |      if (optionVersion.isSet()) {
 | 
		
	
		
			
			| 69 | 86 |          printVersion();
 | 
		
	
		
			
			| 70 | 87 |          return EX_OK;
 | 
		
	
	
		
			
			|  | @@ -107,7 +124,32 @@ int MainClass::main()
 | 
		
	
		
			
			| 107 | 124 |          }
 | 
		
	
		
			
			| 108 | 125 |      }
 | 
		
	
		
			
			| 109 | 126 |  
 | 
		
	
		
			
			|  | 127 | +    std::string inputData = "";
 | 
		
	
		
			
			|  | 128 | +
 | 
		
	
		
			
			| 110 | 129 |      int res = EX_OK;
 | 
		
	
		
			
			|  | 130 | +    Actions action;
 | 
		
	
		
			
			|  | 131 | +    if (optionRead.isSet()) {
 | 
		
	
		
			
			|  | 132 | +        action = Read;
 | 
		
	
		
			
			|  | 133 | +    }
 | 
		
	
		
			
			|  | 134 | +    else if (optionMap.isSet()) {
 | 
		
	
		
			
			|  | 135 | +        action = Map;
 | 
		
	
		
			
			|  | 136 | +    }
 | 
		
	
		
			
			|  | 137 | +    else if (optionWrite.isSet()) {
 | 
		
	
		
			
			|  | 138 | +        auto readResult = readStream(cin());
 | 
		
	
		
			
			|  | 139 | +        if (!readResult) {
 | 
		
	
		
			
			|  | 140 | +            readResult.print();
 | 
		
	
		
			
			|  | 141 | +            return EX_INPUT_ERROR;
 | 
		
	
		
			
			|  | 142 | +        }
 | 
		
	
		
			
			|  | 143 | +        for (auto data : readResult.getData()) {
 | 
		
	
		
			
			|  | 144 | +            inputData += data;
 | 
		
	
		
			
			|  | 145 | +        }
 | 
		
	
		
			
			|  | 146 | +        action = Write;
 | 
		
	
		
			
			|  | 147 | +    }
 | 
		
	
		
			
			|  | 148 | +    else {
 | 
		
	
		
			
			|  | 149 | +        std::cerr << "Must select an action (map|read|write|devices|tags)" << std::endl;
 | 
		
	
		
			
			|  | 150 | +        return EX_USAGE;
 | 
		
	
		
			
			|  | 151 | +    }
 | 
		
	
		
			
			|  | 152 | +
 | 
		
	
		
			
			| 111 | 153 |      LibNfcBusiness libNfc;
 | 
		
	
		
			
			| 112 | 154 |      auto init = libNfc.init();
 | 
		
	
		
			
			| 113 | 155 |      if (!init) {
 | 
		
	
	
		
			
			|  | @@ -171,15 +213,14 @@ int MainClass::main()
 | 
		
	
		
			
			| 171 | 213 |                                  res = EX_NFC_TAG_NOT_FOUND;
 | 
		
	
		
			
			| 172 | 214 |                              }
 | 
		
	
		
			
			| 173 | 215 |                              else {
 | 
		
	
		
			
			| 174 |  | -                                if (optionDump.isSet()) {
 | 
		
	
		
			
			| 175 |  | -                                    res = dump(tag, keys);
 | 
		
	
		
			
			|  | 216 | +                                if (action == Read) {
 | 
		
	
		
			
			|  | 217 | +                                    res = read(tag, keys);
 | 
		
	
		
			
			| 176 | 218 |                                  }
 | 
		
	
		
			
			| 177 |  | -                                else if (optionMap.isSet()) {
 | 
		
	
		
			
			|  | 219 | +                                else if (action == Map) {
 | 
		
	
		
			
			| 178 | 220 |                                      res = mapKeys(tag, keys);
 | 
		
	
		
			
			| 179 | 221 |                                  }
 | 
		
	
		
			
			| 180 |  | -                                else {
 | 
		
	
		
			
			| 181 |  | -                                    std::cerr << "Must select an action (map|read|devices|tags)" << std::endl;
 | 
		
	
		
			
			| 182 |  | -                                    res = EX_USAGE;
 | 
		
	
		
			
			|  | 222 | +                                else if (action == Write) {
 | 
		
	
		
			
			|  | 223 | +                                    res = write(tag, keys, inputData);
 | 
		
	
		
			
			| 183 | 224 |                                  }
 | 
		
	
		
			
			| 184 | 225 |                              }
 | 
		
	
		
			
			| 185 | 226 |                          }
 | 
		
	
	
		
			
			|  | @@ -191,6 +232,12 @@ int MainClass::main()
 | 
		
	
		
			
			| 191 | 232 |          libNfc.clean();
 | 
		
	
		
			
			| 192 | 233 |      }
 | 
		
	
		
			
			| 193 | 234 |  
 | 
		
	
		
			
			|  | 235 | +    if (_outputStream != 0) {
 | 
		
	
		
			
			|  | 236 | +        _outputStream->close();
 | 
		
	
		
			
			|  | 237 | +    }
 | 
		
	
		
			
			|  | 238 | +    if (_inputStream != 0) {
 | 
		
	
		
			
			|  | 239 | +        _inputStream->close();
 | 
		
	
		
			
			|  | 240 | +    }
 | 
		
	
		
			
			| 194 | 241 |      return res;
 | 
		
	
		
			
			| 195 | 242 |  }
 | 
		
	
		
			
			| 196 | 243 |  
 | 
		
	
	
		
			
			|  | @@ -216,17 +263,40 @@ int MainClass::mapKeys(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std
 | 
		
	
		
			
			| 216 | 263 |      return EX_OK;
 | 
		
	
		
			
			| 217 | 264 |  }
 | 
		
	
		
			
			| 218 | 265 |  
 | 
		
	
		
			
			| 219 |  | -int MainClass::dump(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std::string> keys)
 | 
		
	
		
			
			|  | 266 | +int MainClass::read(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std::string> keys)
 | 
		
	
		
			
			|  | 267 | +{
 | 
		
	
		
			
			|  | 268 | +    auto readResult = tag->read(keys, printPercentMapKeys, printPercentDump);
 | 
		
	
		
			
			|  | 269 | +    if (!readResult) {
 | 
		
	
		
			
			|  | 270 | +        readResult.print();
 | 
		
	
		
			
			|  | 271 | +        return EX_READ_ERROR;
 | 
		
	
		
			
			|  | 272 | +    }
 | 
		
	
		
			
			|  | 273 | +    auto read = readResult.getData();
 | 
		
	
		
			
			|  | 274 | +    printSectors(read);
 | 
		
	
		
			
			|  | 275 | +    return EX_OK;
 | 
		
	
		
			
			|  | 276 | +}
 | 
		
	
		
			
			|  | 277 | +
 | 
		
	
		
			
			|  | 278 | +int MainClass::write(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std::string> keys, const std::string &data)
 | 
		
	
		
			
			| 220 | 279 |  {
 | 
		
	
		
			
			| 221 |  | -    auto dumpResult = tag->read(keys, printPercentMapKeys, printPercentDump);
 | 
		
	
		
			
			| 222 |  | -    if (!dumpResult) {
 | 
		
	
		
			
			| 223 |  | -        dumpResult.print();
 | 
		
	
		
			
			| 224 |  | -        return EX_DUMP_ERROR;
 | 
		
	
		
			
			|  | 280 | +    auto writeResult = tag->write(keys, data, false, printPercentMapKeys, printPercentWrite);
 | 
		
	
		
			
			|  | 281 | +    if (!writeResult) {
 | 
		
	
		
			
			|  | 282 | +        writeResult.print();
 | 
		
	
		
			
			|  | 283 | +        return EX_WRITE_ERROR;
 | 
		
	
		
			
			| 225 | 284 |      }
 | 
		
	
		
			
			| 226 |  | -    auto dump = dumpResult.getData();
 | 
		
	
		
			
			| 227 |  | -    for(int s = 0; s < 16; ++s) {
 | 
		
	
		
			
			|  | 285 | +//    std::vector<SectorDbo> sectors;
 | 
		
	
		
			
			|  | 286 | +//    std::string d = StringUtils::ensureSize(data, 1024);
 | 
		
	
		
			
			|  | 287 | +//    for (int i = 0; i < 16; ++i) {
 | 
		
	
		
			
			|  | 288 | +//        SectorDbo sectorDbo(d.substr(i * 64, 64));
 | 
		
	
		
			
			|  | 289 | +//        sectors.push_back(sectorDbo);
 | 
		
	
		
			
			|  | 290 | +//    }
 | 
		
	
		
			
			|  | 291 | +//    printSectors(sectors);
 | 
		
	
		
			
			|  | 292 | +    return EX_OK;
 | 
		
	
		
			
			|  | 293 | +}
 | 
		
	
		
			
			|  | 294 | +
 | 
		
	
		
			
			|  | 295 | +void MainClass::printSectors(const std::vector<SectorDbo> §ors)
 | 
		
	
		
			
			|  | 296 | +{
 | 
		
	
		
			
			|  | 297 | +    for(int s = 0; s < sectors.size(); ++s) {
 | 
		
	
		
			
			| 228 | 298 |          cout() << "+Sector: " << s << std::endl;
 | 
		
	
		
			
			| 229 |  | -        auto sector = dump[s];
 | 
		
	
		
			
			|  | 299 | +        auto sector = sectors[s];
 | 
		
	
		
			
			| 230 | 300 |          for (int b = 0; b < 3; ++b) {
 | 
		
	
		
			
			| 231 | 301 |              cout() << (sector.hasBlock(b) ? StringUtils::rawToHuman(sector.getBlock(b)) : std::string(32, '-')) << std::endl;
 | 
		
	
		
			
			| 232 | 302 |          }
 | 
		
	
	
		
			
			|  | @@ -236,8 +306,8 @@ int MainClass::dump(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std::s
 | 
		
	
		
			
			| 236 | 306 |  
 | 
		
	
		
			
			| 237 | 307 |  
 | 
		
	
		
			
			| 238 | 308 |          cout() << "+Trailer key A: " << (sector.hasKeyA() ? StringUtils::rawToHuman(sector.getKeyA()) : std::string(12, '-'))
 | 
		
	
		
			
			| 239 |  | -            << "\t AC bits: " << (sector.hasAccessBits() ? StringUtils::rawToHuman(sector.getAccessBits()) : std::string(8, '-'))
 | 
		
	
		
			
			| 240 |  | -            << "\t key B: " << (sector.hasKeyB() ? StringUtils::rawToHuman(sector.getKeyB()) : std::string(12, '-')) << std::endl;
 | 
		
	
		
			
			|  | 309 | +        << "\t AC bits: " << (sector.hasAccessBits() ? StringUtils::rawToHuman(sector.getAccessBits()) : std::string(8, '-'))
 | 
		
	
		
			
			|  | 310 | +        << "\t key B: " << (sector.hasKeyB() ? StringUtils::rawToHuman(sector.getKeyB()) : std::string(12, '-')) << std::endl;
 | 
		
	
		
			
			| 241 | 311 |          AccessBitsDbo accessBitsDbo = sector.getAccessBitsDbo();
 | 
		
	
		
			
			| 242 | 312 |          for (int b = 0; b < 3; ++b) {
 | 
		
	
		
			
			| 243 | 313 |              cout() << "+Block: " << b << " ";
 | 
		
	
	
		
			
			|  | @@ -246,7 +316,6 @@ int MainClass::dump(std::shared_ptr<FreeFareTagBusiness> tag, std::vector<std::s
 | 
		
	
		
			
			| 246 | 316 |          cout() << "+Block: 3 ";
 | 
		
	
		
			
			| 247 | 317 |          printTrailerAccessBits(accessBitsDbo);
 | 
		
	
		
			
			| 248 | 318 |      }
 | 
		
	
		
			
			| 249 |  | -    return EX_OK;
 | 
		
	
		
			
			| 250 | 319 |  }
 | 
		
	
		
			
			| 251 | 320 |  
 | 
		
	
		
			
			| 252 | 321 |  void MainClass::printBlockAccessBits(const AccessBitsDbo &accessBits, int block)
 | 
		
	
	
		
			
			|  | @@ -290,6 +359,11 @@ void MainClass::printPercentDump(int done, int total)
 | 
		
	
		
			
			| 290 | 359 |      printPercent(done, total, "Dumping");
 | 
		
	
		
			
			| 291 | 360 |  }
 | 
		
	
		
			
			| 292 | 361 |  
 | 
		
	
		
			
			|  | 362 | +void MainClass::printPercentWrite(int done, int total)
 | 
		
	
		
			
			|  | 363 | +{
 | 
		
	
		
			
			|  | 364 | +    printPercent(done, total, "Writing");
 | 
		
	
		
			
			|  | 365 | +}
 | 
		
	
		
			
			|  | 366 | +
 | 
		
	
		
			
			| 293 | 367 |  void MainClass::printVersion()
 | 
		
	
		
			
			| 294 | 368 |  {
 | 
		
	
		
			
			| 295 | 369 |      cout() << "LibNfc version: " << LibNfcBusiness::getLibNfcVersion() << std::endl;
 | 
		
	
	
		
			
			|  | @@ -333,26 +407,32 @@ std::shared_ptr<FreeFareTagBusiness> MainClass::getTag(const std::string &tagUid
 | 
		
	
		
			
			| 333 | 407 |  
 | 
		
	
		
			
			| 334 | 408 |  Result<std::vector<std::string>> MainClass::readFile(const std::string &filePath)
 | 
		
	
		
			
			| 335 | 409 |  {
 | 
		
	
		
			
			| 336 |  | -    std::vector<std::string> lines;
 | 
		
	
		
			
			| 337 | 410 |      std::ifstream fileInput(filePath);
 | 
		
	
		
			
			| 338 | 411 |      if (fileInput) {
 | 
		
	
		
			
			| 339 |  | -        while (!fileInput.eof()) {
 | 
		
	
		
			
			| 340 |  | -            std::string line;
 | 
		
	
		
			
			| 341 |  | -            std::getline(fileInput, line);
 | 
		
	
		
			
			| 342 |  | -            line = StringUtils::removeSpaces(line);
 | 
		
	
		
			
			| 343 |  | -            if (line.compare(0, 1, "#") != 0 && line.compare(0, 1, "+") != 0) {
 | 
		
	
		
			
			| 344 |  | -                auto keyResult = StringUtils::humanToRaw(line);
 | 
		
	
		
			
			| 345 |  | -                if (!keyResult) {
 | 
		
	
		
			
			| 346 |  | -                    return Result<std::vector<std::string>>::error("Invalid file data");
 | 
		
	
		
			
			| 347 |  | -                }
 | 
		
	
		
			
			| 348 |  | -                line = keyResult.getData();
 | 
		
	
		
			
			| 349 |  | -                lines.push_back(line);
 | 
		
	
		
			
			| 350 |  | -            }
 | 
		
	
		
			
			| 351 |  | -        }
 | 
		
	
		
			
			|  | 412 | +        return readStream(fileInput);
 | 
		
	
		
			
			| 352 | 413 |      }
 | 
		
	
		
			
			| 353 | 414 |      else {
 | 
		
	
		
			
			| 354 | 415 |          return Result<std::vector<std::string>>::error("Failed to open file: " + std::string(strerror(errno)));
 | 
		
	
		
			
			| 355 | 416 |      }
 | 
		
	
		
			
			|  | 417 | +}
 | 
		
	
		
			
			|  | 418 | +
 | 
		
	
		
			
			|  | 419 | +Result<std::vector<std::string>> MainClass::readStream(std::istream &stream)
 | 
		
	
		
			
			|  | 420 | +{
 | 
		
	
		
			
			|  | 421 | +    std::vector<std::string> lines;
 | 
		
	
		
			
			|  | 422 | +    while (!stream.eof()) {
 | 
		
	
		
			
			|  | 423 | +        std::string line;
 | 
		
	
		
			
			|  | 424 | +        std::getline(stream, line);
 | 
		
	
		
			
			|  | 425 | +        line = StringUtils::removeSpaces(line);
 | 
		
	
		
			
			|  | 426 | +        if (line.compare(0, 1, "#") != 0 && line.compare(0, 1, "+") != 0) {
 | 
		
	
		
			
			|  | 427 | +            std::replace(line.begin(), line.end(), '-', '0');
 | 
		
	
		
			
			|  | 428 | +            auto keyResult = StringUtils::humanToRaw(line);
 | 
		
	
		
			
			|  | 429 | +            if (!keyResult) {
 | 
		
	
		
			
			|  | 430 | +                return Result<std::vector<std::string>>::error("Invalid data");
 | 
		
	
		
			
			|  | 431 | +            }
 | 
		
	
		
			
			|  | 432 | +            line = keyResult.getData();
 | 
		
	
		
			
			|  | 433 | +            lines.push_back(line);
 | 
		
	
		
			
			|  | 434 | +        }
 | 
		
	
		
			
			|  | 435 | +    }
 | 
		
	
		
			
			| 356 | 436 |      return Result<std::vector<std::string>>::ok(lines);
 | 
		
	
		
			
			| 357 | 437 |  }
 | 
		
	
		
			
			| 358 | 438 |  
 | 
		
	
	
		
			
			|  | @@ -360,3 +440,8 @@ std::ostream &MainClass::cout()
 | 
		
	
		
			
			| 360 | 440 |  {
 | 
		
	
		
			
			| 361 | 441 |      return _outputStream == 0 ? std::cout : *_outputStream;
 | 
		
	
		
			
			| 362 | 442 |  }
 | 
		
	
		
			
			|  | 443 | +
 | 
		
	
		
			
			|  | 444 | +std::istream &MainClass::cin()
 | 
		
	
		
			
			|  | 445 | +{
 | 
		
	
		
			
			|  | 446 | +    return _inputStream == 0 ? std::cin : *_inputStream;
 | 
		
	
		
			
			|  | 447 | +}
 |