/** * Verifies that the MAR file matches the current product, channel, and version * * @param MARChannelID The MAR channel name to use, only updates from MARs * with a matching MAR channel name will succeed. * If an empty string is passed, no check will be done * for the channel name in the product information block. * If a comma separated list of values is passed then * one value must match. * @param appVersion The application version to use, only MARs with an * application version >= to appVersion will be applied. * @return OK on success * COULD_NOT_READ_PRODUCT_INFO_BLOCK if the product info block * could not be read. * MARCHANNEL_MISMATCH_ERROR if update-settings.ini's MAR * channel ID doesn't match the MAR * file's MAR channel ID. * VERSION_DOWNGRADE_ERROR if the application version for * this updater is newer than the * one in the MAR. */ int ArchiveReader::VerifyProductInformation(const char *MARChannelID, const char *appVersion) { if (!mArchive) { return ARCHIVE_NOT_OPEN; } ProductInformationBlock productInfoBlock; int rv = mar_read_product_info_block(mArchive, &productInfoBlock); if (rv != OK) { return COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR; } // Only check the MAR channel name if specified, it should be passed in from // the update-settings.ini file. if (MARChannelID && strlen(MARChannelID)) { // Check for at least one match in the comma separated list of values. const char *delimiter = " ,\t"; // Make a copy of the string in case a read only memory buffer // was specified. strtok modifies the input buffer. char channelCopy[512] = { 0 }; strncpy(channelCopy, MARChannelID, sizeof(channelCopy) - 1); char *channel = strtok(channelCopy, delimiter); rv = MAR_CHANNEL_MISMATCH_ERROR; while(channel) { if (!strcmp(channel, productInfoBlock.MARChannelID)) { rv = OK; break; } channel = strtok(nullptr, delimiter); } } if (rv == OK) { /* Compare both versions to ensure we don't have a downgrade -1 if appVersion is older than productInfoBlock.productVersion 1 if appVersion is newer than productInfoBlock.productVersion 0 if appVersion is the same as productInfoBlock.productVersion This even works with strings like: - 12.0a1 being older than 12.0a2 - 12.0a2 being older than 12.0b1 - 12.0a1 being older than 12.0 - 12.0 being older than 12.1a1 */ int versionCompareResult = mozilla::CompareVersions(appVersion, productInfoBlock.productVersion); if (1 == versionCompareResult) { rv = VERSION_DOWNGRADE_ERROR; } } free((void *)productInfoBlock.MARChannelID); free((void *)productInfoBlock.productVersion); return rv; }
/** * Reads the product info block from the MAR file's additional block section. * The caller is responsible for freeing the fields in infoBlock * if the return is successful. * * @param infoBlock Out parameter for where to store the result to * @return 0 on success, -1 on failure */ int read_product_info_block(char *path, struct ProductInformationBlock *infoBlock) { int rv; MarFile mar; mar.fp = fopen(path, "rb"); if (!mar.fp) { return -1; } rv = mar_read_product_info_block(&mar, infoBlock); fclose(mar.fp); return rv; }
/** * Reads the product info block from the MAR file's additional block section. * The caller is responsible for freeing the fields in infoBlock * if the return is successful. * * @param infoBlock Out parameter for where to store the result to * @return 0 on success, -1 on failure */ int read_product_info_block(char *path, struct ProductInformationBlock *infoBlock) { int rv; MarFile mar; mar.fp = fopen(path, "rb"); if (!mar.fp) { fprintf(stderr, "ERROR: could not open file in read_product_info_block()\n"); perror(path); return -1; } rv = mar_read_product_info_block(&mar, infoBlock); fclose(mar.fp); return rv; }