/* * Replace all 'c' characters in string 'src' with the subtsring 'rep' * The returned string is allocated and must be freed by the caller. */ char* replace_char(const char* src, const char c, const char* rep) { size_t i, j, k, count=0, str_len = safe_strlen(src), rep_len = safe_strlen(rep); char* res; if ((src == NULL) || (rep == NULL)) return NULL; for (i=0; i<str_len; i++) { if (src[i] == c) count++; } res = (char*)malloc(str_len + count*rep_len + 1); if (res == NULL) return NULL; for (i=0,j=0; i<str_len; i++) { if (src[i] == c) { for(k=0; k<rep_len; k++) res[j++] = rep[k]; } else { res[j++] = src[i]; } } res[j] = 0; return res; }
/** A safe cat of a string. */ void safe_cat_string(char* dest, size_t dest_len, const char* cat) { size_t current_len = safe_strlen(dest); size_t input_len = safe_strlen(cat); if (dest_len < current_len + input_len + 1) { printf("Error catting string!\n"); return; } memcpy(dest + current_len, cat, input_len); dest[current_len + input_len] = '\0'; }
static link_type get_link_type(cmark_node *node) { size_t title_len, url_len; cmark_node *link_text; char *realurl; int realurllen; bool isemail = false; if (node->type != CMARK_NODE_LINK) { return NO_LINK; } const char* url = cmark_node_get_url(node); cmark_chunk url_chunk = cmark_chunk_literal(url); url_len = safe_strlen(url); if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) { return NO_LINK; } const char* title = cmark_node_get_title(node); title_len = safe_strlen(title); // if it has a title, we can't treat it as an autolink: if (title_len > 0) { return NORMAL_LINK; } link_text = node->first_child; cmark_consolidate_text_nodes(link_text); realurl = (char*)url; realurllen = url_len; if (strncmp(realurl, "mailto:", 7) == 0) { realurl += 7; realurllen -= 7; isemail = true; } if (realurllen == link_text->as.literal.len && strncmp(realurl, (char*)link_text->as.literal.data, link_text->as.literal.len) == 0) { if (isemail) { return EMAIL_AUTOLINK; } else { return URL_AUTOLINK; } } else { return NORMAL_LINK; } }
static int shortest_unused_backtick_sequence(const char *code) { int32_t used = 1; int current = 0; size_t i = 0; size_t code_len = safe_strlen(code); while (i <= code_len) { if (code[i] == '`') { current++; } else { if (current) { used |= (1 << current); } current = 0; } i++; } // return number of first bit that is 0: i = 0; while (used & 1) { used = used >> 1; i++; } return i; }
/* * Open a localization file and store its file name, with special case * when dealing with the embedded loc file. */ FILE* open_loc_file(const char* filename) { FILE* fd = NULL; wchar_t *wfilename = NULL; const char* tmp_ext = ".tmp"; if (filename == NULL) return NULL; if (loc_filename != embedded_loc_filename) { safe_free(loc_filename); } if (safe_strcmp(tmp_ext, &filename[safe_strlen(filename)-4]) == 0) { loc_filename = embedded_loc_filename; } else { loc_filename = safe_strdup(filename); } wfilename = utf8_to_wchar(filename); if (wfilename == NULL) { uprintf(conversion_error, filename); goto out; } fd = _wfopen(wfilename, L"rb"); if (fd == NULL) { uprintf("localization: could not open '%s'\n", filename); } out: safe_free(wfilename); return fd; }
/* * Return the next unused drive letter from the system */ char GetUnusedDriveLetter(void) { DWORD size; char drive_letter = 'Z'+1, *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)\n", size, sizeof(drives)); goto out; } for (drive_letter = 'C'; drive_letter < 'Z'; drive_letter++) { for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; if (drive_letter == (char)toupper((int)*drive)) break; } if (!*drive) break; } out: return (drive_letter>'Z')?0:drive_letter; }
const char *windows_error_str(DWORD retval) { static char err_string[ERR_BUFFER_SIZE]; DWORD error_code, format_error; DWORD size; ssize_t i; error_code = retval ? retval : GetLastError(); safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", (unsigned int)error_code); // Translate codes returned by SetupAPI. The ones we are dealing with are either // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes. // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx switch (error_code & 0xE0000000) { case 0: error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified break; case 0xE0000000: error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); break; default: break; } size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); if (size == 0) { format_error = GetLastError(); if (format_error) safe_sprintf(err_string, ERR_BUFFER_SIZE, "Windows error code %u (FormatMessage error code %u)", (unsigned int)error_code, (unsigned int)format_error); else safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code); } else { // Remove CR/LF terminators for (i = safe_strlen(err_string) - 1; (i >= 0) && ((err_string[i] == 0x0A) || (err_string[i] == 0x0D)); i--) err_string[i] = 0; } return err_string; }
/* * Initial update check setup */ BOOL SetUpdateCheck(void) { BOOL enable_updates; DWORD commcheck = GetTickCount(); notification_info more_info = { IDD_UPDATE_POLICY, UpdateCallback }; char filename[MAX_PATH] = "", exename[] = APPLICATION_NAME ".exe"; size_t fn_len, exe_len; // Test if we have access to the registry. If not, forget it. WriteRegistryKey32(REGKEY_HKCU, REGKEY_COMM_CHECK, commcheck); if (ReadRegistryKey32(REGKEY_HKCU, REGKEY_COMM_CHECK) != commcheck) return FALSE; reg_commcheck = TRUE; // If the update interval is not set, this is the first time we run so prompt the user if (ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL) == 0) { // Add a hack for people who'd prefer the app not to prompt about update settings on first run. // If the executable is called "<app_name>.exe", without version, we disable the prompt GetModuleFileNameU(NULL, filename, sizeof(filename)); fn_len = safe_strlen(filename); exe_len = safe_strlen(exename); if ((fn_len > exe_len) && (safe_stricmp(&filename[fn_len-exe_len], exename) == 0)) { dprintf("Short name used - Disabling initial update policy prompt\n"); enable_updates = TRUE; } else { enable_updates = notification(MSG_QUESTION, &more_info, APPLICATION_NAME " update policy", "Do you want to allow " APPLICATION_NAME " to check for application updates online?"); } if (!enable_updates) { WriteRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL, -1); return FALSE; } // If the user hasn't set the interval in the dialog, set to default if ( (ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL) == 0) || ((ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL) == -1) && enable_updates) ) WriteRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL, 86400); } return TRUE; }
// Apply various workarounds to Linux config files static void fix_config(const char* psz_fullpath, const char* psz_path, const char* psz_basename, EXTRACT_PROPS* props) { size_t i, nul_pos; char *iso_label = NULL, *usb_label = NULL, *src, *dst; nul_pos = safe_strlen(psz_fullpath); src = safe_strdup(psz_fullpath); if (src == NULL) return; for (i=0; i<nul_pos; i++) if (src[i] == '/') src[i] = '\\'; // Workaround for config files requiring an ISO label for kernel append that may be // different from our USB label. Oh, and these labels must have spaces converted to \x20. if ((props->is_syslinux_cfg) || (props->is_grub_cfg)) { iso_label = replace_char(img_report.label, ' ', "\\x20"); usb_label = replace_char(img_report.usb_label, ' ', "\\x20"); if ((iso_label != NULL) && (usb_label != NULL)) { if (replace_in_token_data(src, (props->is_syslinux_cfg) ? "append" : "linuxefi", iso_label, usb_label, TRUE) != NULL) uprintf(" Patched %s: '%s' ⇨ '%s'\n", src, iso_label, usb_label); } safe_free(iso_label); safe_free(usb_label); } // Fix dual BIOS + EFI support for tails and other ISOs if ( (props->is_syslinux_cfg) && (safe_stricmp(psz_path, efi_dirname) == 0) && (safe_stricmp(psz_basename, syslinux_cfg[0]) == 0) && (!img_report.has_efi_syslinux) && (dst = safe_strdup(src)) ) { dst[nul_pos-12] = 's'; dst[nul_pos-11] = 'y'; dst[nul_pos-10] = 's'; CopyFileA(src, dst, TRUE); uprintf("Duplicated %s to %s\n", src, dst); free(dst); } // Workaround for FreeNAS if (props->is_grub_cfg) { iso_label = malloc(MAX_PATH); usb_label = malloc(MAX_PATH); if ((iso_label != NULL) && (usb_label != NULL)) { safe_sprintf(iso_label, MAX_PATH, "cd9660:/dev/iso9660/%s", img_report.label); safe_sprintf(usb_label, MAX_PATH, "msdosfs:/dev/msdosfs/%s", img_report.usb_label); if (replace_in_token_data(src, "set", iso_label, usb_label, TRUE) != NULL) uprintf(" Patched %s: '%s' ⇨ '%s'\n", src, iso_label, usb_label); } safe_free(iso_label); safe_free(usb_label); } free(src); }
static __inline char* get_sanitized_token_data_buffer(const char* token, unsigned int n, const char* buffer, size_t buffer_size) { size_t i; char* data = get_token_data_buffer(token, n, buffer, buffer_size); if (data != NULL) { for (i=0; i<safe_strlen(data); i++) { if ((data[i] == '\\') && (data[i+1] == 'n')) { data[i] = '\r'; data[i+1] = '\n'; } } } return data; }
// Ensure filenames do not contain invalid FAT32 or NTFS characters static __inline BOOL sanitize_filename(char* filename) { size_t i, j; BOOL ret = FALSE; char unauthorized[] = {'<', '>', ':', '|', '*', '?'}; // Must start after the drive part (D:\...) so that we don't eliminate the first column for (i=2; i<safe_strlen(filename); i++) { for (j=0; j<sizeof(unauthorized); j++) { if (filename[i] == unauthorized[j]) { filename[i] = '_'; ret = TRUE; } } } return ret; }
/** build a URL, returned char* needs to be free'd */ char* pt_changes_feed_build_url(const char* server_name, const char* db, const pt_changes_feed handle) { char *ret_str; char* end_of_string; size_t size_of_string = safe_strlen(server_name) + safe_strlen(db) + safe_strlen(handle->extra_opts) + safe_strlen("//_changes?heartbeat=&feed=continuous") + 2048; ret_str = calloc(1, size_of_string); // Add the changes feed base URL snprintf(ret_str, size_of_string, "%s/%s/_changes", server_name, db); if (!handle->continuous && !handle->heartbeats && !safe_strlen(handle->extra_opts)) return ret_str; // If we get here, it means that we have added options. There is *no* // checking at this point! safe_cat_string(ret_str, size_of_string, "?"); // Hearbeat if (handle->heartbeats) { snprintf(ret_str + safe_strlen(ret_str),size_of_string - safe_strlen(ret_str), "heartbeat=%i&",handle->heartbeats); } // Continuous if (handle->continuous) safe_cat_string(ret_str, size_of_string, "feed=continuous&"); // Adding extra options passed by the user if (safe_strlen(handle->extra_opts)) safe_cat_string(ret_str, size_of_string, handle->extra_opts); // If we have an ampersand on the end, remove it end_of_string = &ret_str[safe_strlen(ret_str)-1]; if (*end_of_string == '&') *end_of_string = '\0'; return ret_str; }
/* * Parse an update data file and populates a rufus_update structure. * NB: since this is remote data, and we're running elevated, it *IS* considered * potentially malicious, even if it comes from a supposedly trusted server. * len should be the size of the buffer, including the zero terminator */ void parse_update(char* buf, size_t len) { size_t i; char *data = NULL, *token; char allowed_rtf_chars[] = "abcdefghijklmnopqrstuvwxyz|~-_:*'"; char allowed_std_chars[] = "\r\n ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&+=<>(){}[].,;#@/?"; // strchr includes the NUL terminator in the search, so take care of backslash before NUL if ((buf == NULL) || (len < 2) || (len > 65536) || (buf[len-1] != 0) || (buf[len-2] == '\\')) return; // Sanitize the data - Not a silver bullet, but it helps len = safe_strlen(buf)+1; // Someone may be inserting NULs for (i=0; i<len-1; i++) { // Check for valid RTF sequences as well as allowed chars if not RTF if (buf[i] == '\\') { // NB: we have a zero terminator, so we can afford a +1 without overflow if (strchr(allowed_rtf_chars, buf[i+1]) == NULL) { buf[i] = ' '; } } else if ((strchr(allowed_rtf_chars, buf[i]) == NULL) && (strchr(allowed_std_chars, buf[i]) == NULL)) { buf[i] = ' '; } } for (i=0; i<3; i++) update.version[i] = 0; update.platform_min[0] = 5; update.platform_min[1] = 2; // XP or later safe_free(update.download_url); safe_free(update.release_notes); if ((data = get_sanitized_token_data_buffer("version", 1, buf, len)) != NULL) { for (i=0; (i<3) && ((token = strtok((i==0)?data:NULL, ".")) != NULL); i++) { update.version[i] = (uint16_t)atoi(token); } safe_free(data); } if ((data = get_sanitized_token_data_buffer("platform_min", 1, buf, len)) != NULL) { for (i=0; (i<2) && ((token = strtok((i==0)?data:NULL, ".")) != NULL); i++) { update.platform_min[i] = (uint32_t)atoi(token); } safe_free(data); } update.download_url = get_sanitized_token_data_buffer("download_url", 1, buf, len); update.release_notes = get_sanitized_token_data_buffer("release_notes", 1, buf, len); }
static int longest_backtick_sequence(const char *code) { int longest = 0; int current = 0; size_t i = 0; size_t code_len = safe_strlen(code); while (i <= code_len) { if (code[i] == '`') { current++; } else { if (current > longest) { longest = current; } current = 0; } i++; } return longest; }
/* * Send individual lines of the syslog section pointed by buffer back to the main application * xbuffer's payload MUST start at byte 1 to accomodate the SYSLOG_MESSAGE prefix */ DWORD process_syslog(char* buffer, DWORD size) { DWORD i, write_size, junk, start = 0; char* xbuffer; char* ins_string = "<ins>"; char conversion_error[] = " ERROR: Unable to convert log entry to UTF-8"; if (buffer == NULL) return 0; // CR/LF breakdown for (i=0; i<size; i++) { if ((buffer[i] == 0x0D) || (buffer[i] == 0x0A)) { write_size = i-start + 1; do { buffer[i++] = 0; } while ( ((buffer[i] == 0x0D) || (buffer[i] == 0x0A)) && (i <= size) ); // The setupapi.dev.log uses a dubious method to mark its current position // If there's any "<ins>" line in any log file, it's game over then if (safe_strcmp(ins_string, buffer + start) == 0) { return start; } // The logs are using the system locale. Convert to UTF8 (with extra leading byte) xbuffer = xlocale_to_utf8(&buffer[start]); if (xbuffer == NULL) { xbuffer = conversion_error; } // This is where we use the extra start byte xbuffer[0] = IC_SYSLOG_MESSAGE; WriteFile(pipe_handle, xbuffer, (DWORD)safe_strlen(&xbuffer[1])+2, &junk, NULL); if (xbuffer != conversion_error) { free(xbuffer); } start = i; } } // start does not necessarily equate size, if there are truncated lines at the end return start; }
// Ensure filenames do not contain invalid FAT32 or NTFS characters static __inline char* sanitize_filename(char* filename, BOOL* is_identical) { size_t i, j; char* ret = NULL; char unauthorized[] = {'<', '>', ':', '|', '*', '?'}; *is_identical = TRUE; ret = safe_strdup(filename); if (ret == NULL) { uprintf("Could not allocate string for sanitized path"); return NULL; } // Must start after the drive part (D:\...) so that we don't eliminate the first column for (i=2; i<safe_strlen(ret); i++) { for (j=0; j<sizeof(unauthorized); j++) { if (ret[i] == unauthorized[j]) { ret[i] = '_'; *is_identical = FALSE; } } } return ret; }
// Returns 0 on success, nonzero on error static int iso_extract_files(iso9660_t* p_iso, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; BOOL s, is_syslinux_cfg, is_old_c32[NB_OLD_C32], is_symlink; int i_length, r = 1; char tmp[128], psz_fullpath[1024], *psz_basename; const char *psz_iso_name = &psz_fullpath[strlen(psz_extract_dir)]; unsigned char buf[ISO_BLOCKSIZE]; CdioListNode_t* p_entnode; iso9660_stat_t *p_statbuf; CdioList_t* p_entlist; size_t i, j, nul_pos; lsn_t lsn; int64_t i_file_length; if ((p_iso == NULL) || (psz_path == NULL)) return 1; i_length = _snprintf(psz_fullpath, sizeof(psz_fullpath), "%s%s/", psz_extract_dir, psz_path); if (i_length < 0) return 1; psz_basename = &psz_fullpath[i_length]; p_entlist = iso9660_ifs_readdir(p_iso, psz_path); if (!p_entlist) { uprintf("Could not access directory %s\n", psz_path); return 1; } _CDIO_LIST_FOREACH(p_entnode, p_entlist) { if (FormatStatus) goto out; p_statbuf = (iso9660_stat_t*) _cdio_list_node_data(p_entnode); // Eliminate . and .. entries if ( (strcmp(p_statbuf->filename, ".") == 0) || (strcmp(p_statbuf->filename, "..") == 0) ) continue; // Rock Ridge requires an exception is_symlink = FALSE; if ((p_statbuf->rr.b3_rock == yep) && enable_rockridge) { safe_strcpy(psz_basename, sizeof(psz_fullpath)-i_length-1, p_statbuf->filename); if (safe_strlen(p_statbuf->filename) > 64) iso_report.has_long_filename = TRUE; // libcdio has a memleak for Rock Ridge symlinks. It doesn't look like there's an easy fix there as // a generic list that's unaware of RR extensions is being used, so we prevent that memleak ourselves is_symlink = (p_statbuf->rr.psz_symlink != NULL); if (is_symlink) iso_report.has_symlinks = TRUE; if (scan_only) safe_free(p_statbuf->rr.psz_symlink); } else { iso9660_name_translate_ext(p_statbuf->filename, psz_basename, i_joliet_level); } if (p_statbuf->type == _STAT_DIR) { if (!scan_only) _mkdirU(psz_fullpath); if (iso_extract_files(p_iso, psz_iso_name)) goto out; } else { i_file_length = p_statbuf->size; if (check_iso_props(psz_path, &is_syslinux_cfg, is_old_c32, i_file_length, psz_basename, psz_fullpath)) { continue; } // Replace slashes with backslashes and append the size to the path for UI display nul_pos = safe_strlen(psz_fullpath); for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '/') psz_fullpath[i] = '\\'; safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE)); uprintf("Extracting: %s\n", psz_fullpath); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE)); SetWindowTextU(hISOFileName, psz_fullpath); // ISO9660 cannot handle backslashes for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '\\') psz_fullpath[i] = '/'; psz_fullpath[nul_pos] = 0; for (i=0; i<NB_OLD_C32; i++) { if (is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileA(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version\n"); break; } uprintf(" Could not replace file: %s\n", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; if (sanitize_filename(psz_fullpath)) uprintf(" File name sanitized to '%s'\n", psz_fullpath); if (is_symlink) { if (i_file_length == 0) uprintf(" Ignoring Rock Ridge symbolic link to '%s'\n", p_statbuf->rr.psz_symlink); safe_free(p_statbuf->rr.psz_symlink); } file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s\n", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_fullpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else for (i=0; i_file_length>0; i++) { if (FormatStatus) goto out; memset(buf, 0, ISO_BLOCKSIZE); lsn = p_statbuf->lsn + (lsn_t)i; if (iso9660_iso_seek_read(p_iso, buf, lsn, 1) != ISO_BLOCKSIZE) { uprintf(" Error reading ISO9660 file %s at LSN %lu\n", psz_iso_name, (long unsigned int)lsn); goto out; } buf_size = (DWORD)MIN(i_file_length, ISO_BLOCKSIZE); for (j=0; j<WRITE_RETRIES; j++) { ISO_BLOCKING(s = WriteFile(file_handle, buf, buf_size, &wr_size, NULL)); if ((!s) || (buf_size != wr_size)) { uprintf(" Error writing file: %s", WindowsErrorString()); if (j < WRITE_RETRIES-1) uprintf(" RETRYING...\n"); } else { break; } } if (j >= WRITE_RETRIES) goto out; i_file_length -= ISO_BLOCKSIZE; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) { SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0); UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } } ISO_BLOCKING(safe_closehandle(file_handle)); if (is_syslinux_cfg) { if (replace_in_token_data(psz_fullpath, "append", iso_report.label, iso_report.usb_label, TRUE) != NULL) uprintf("Patched %s: '%s' -> '%s'\n", psz_fullpath, iso_report.label, iso_report.usb_label); } } } r = 0; out: ISO_BLOCKING(safe_closehandle(file_handle)); _cdio_list_free(p_entlist, true); return r; }
// Returns 0 on success, nonzero on error static int udf_extract_files(udf_t *p_udf, udf_dirent_t *p_udf_dirent, const char *psz_path) { HANDLE file_handle = NULL; DWORD buf_size, wr_size, err; BOOL r, is_syslinux_cfg, is_old_c32[NB_OLD_C32]; int i_length; size_t i, nul_pos; char tmp[128], *psz_fullpath = NULL; const char* psz_basename; udf_dirent_t *p_udf_dirent2; uint8_t buf[UDF_BLOCKSIZE]; int64_t i_read, i_file_length; if ((p_udf_dirent == NULL) || (psz_path == NULL)) return 1; while ((p_udf_dirent = udf_readdir(p_udf_dirent)) != NULL) { if (FormatStatus) goto out; psz_basename = udf_get_filename(p_udf_dirent); if (strlen(psz_basename) == 0) continue; i_length = (int)(3 + strlen(psz_path) + strlen(psz_basename) + strlen(psz_extract_dir) + 24); psz_fullpath = (char*)calloc(sizeof(char), i_length); if (psz_fullpath == NULL) { uprintf("Error allocating file name\n"); goto out; } i_length = _snprintf(psz_fullpath, i_length, "%s%s/%s", psz_extract_dir, psz_path, psz_basename); if (i_length < 0) { goto out; } if (udf_is_dir(p_udf_dirent)) { if (!scan_only) _mkdirU(psz_fullpath); p_udf_dirent2 = udf_opendir(p_udf_dirent); if (p_udf_dirent2 != NULL) { if (udf_extract_files(p_udf, p_udf_dirent2, &psz_fullpath[strlen(psz_extract_dir)])) goto out; } } else { i_file_length = udf_get_file_length(p_udf_dirent); if (check_iso_props(psz_path, &is_syslinux_cfg, is_old_c32, i_file_length, psz_basename, psz_fullpath)) { safe_free(psz_fullpath); continue; } // Replace slashes with backslashes and append the size to the path for UI display nul_pos = safe_strlen(psz_fullpath); for (i=0; i<nul_pos; i++) if (psz_fullpath[i] == '/') psz_fullpath[i] = '\\'; safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, TRUE, FALSE)); uprintf("Extracting: %s\n", psz_fullpath); safe_sprintf(&psz_fullpath[nul_pos], 24, " (%s)", SizeToHumanReadable(i_file_length, FALSE, FALSE)); SetWindowTextU(hISOFileName, psz_fullpath); // Remove the appended size for extraction psz_fullpath[nul_pos] = 0; for (i=0; i<NB_OLD_C32; i++) { if (is_old_c32[i] && use_own_c32[i]) { static_sprintf(tmp, "%s/syslinux-%s/%s", FILES_DIR, embedded_sl_version_str[0], old_c32_name[i]); if (CopyFileA(tmp, psz_fullpath, FALSE)) { uprintf(" Replaced with local version\n"); break; } uprintf(" Could not replace file: %s\n", WindowsErrorString()); } } if (i < NB_OLD_C32) continue; if (sanitize_filename(psz_fullpath)) uprintf(" File name sanitized to '%s'\n", psz_fullpath); file_handle = CreateFileU(psz_fullpath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { err = GetLastError(); uprintf(" Unable to create file: %s\n", WindowsErrorString()); if ((err == ERROR_ACCESS_DENIED) && (safe_strcmp(&psz_fullpath[3], autorun_name) == 0)) uprintf(stupid_antivirus); else goto out; } else while (i_file_length > 0) { if (FormatStatus) goto out; memset(buf, 0, UDF_BLOCKSIZE); i_read = udf_read_block(p_udf_dirent, buf, 1); if (i_read < 0) { uprintf(" Error reading UDF file %s\n", &psz_fullpath[strlen(psz_extract_dir)]); goto out; } buf_size = (DWORD)MIN(i_file_length, i_read); for (i=0; i<WRITE_RETRIES; i++) { ISO_BLOCKING(r = WriteFile(file_handle, buf, buf_size, &wr_size, NULL)); if ((!r) || (buf_size != wr_size)) { uprintf(" Error writing file: %s", WindowsErrorString()); if (i < WRITE_RETRIES-1) uprintf(" RETRYING...\n"); } else { break; } } if (i >= WRITE_RETRIES) goto out; i_file_length -= i_read; if (nb_blocks++ % PROGRESS_THRESHOLD == 0) { SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)((MAX_PROGRESS*nb_blocks)/total_blocks), 0); UpdateProgress(OP_DOS, 100.0f*nb_blocks/total_blocks); } } // If you have a fast USB 3.0 device, the default Windows buffering does an // excellent job at compensating for our small blocks read/writes to max out the // device's bandwidth. // The drawback however is with cancellation. With a large file, CloseHandle() // may take forever to complete and is not interruptible. We try to detect this. ISO_BLOCKING(safe_closehandle(file_handle)); if (is_syslinux_cfg) { // Workaround for isolinux config files requiring an ISO label for kernel // append that may be different from our USB label. if (replace_in_token_data(psz_fullpath, "append", iso_report.label, iso_report.usb_label, TRUE) != NULL) uprintf("Patched %s: '%s' -> '%s'\n", psz_fullpath, iso_report.label, iso_report.usb_label); } } safe_free(psz_fullpath); } return 0; out: if (p_udf_dirent != NULL) udf_dirent_free(p_udf_dirent); ISO_BLOCKING(safe_closehandle(file_handle)); safe_free(psz_fullpath); return 1; }
/* * Scan and set ISO properties * Returns true if the the current file does not need to be processed further */ static BOOL check_iso_props(const char* psz_dirname, BOOL* is_syslinux_cfg, BOOL* is_old_c32, int64_t i_file_length, const char* psz_basename, const char* psz_fullpath) { size_t i, j; // Check for an isolinux/syslinux config file anywhere *is_syslinux_cfg = FALSE; for (i=0; i<ARRAYSIZE(syslinux_cfg); i++) { if (safe_stricmp(psz_basename, syslinux_cfg[i]) == 0) *is_syslinux_cfg = TRUE; } // Check for a syslinux v5.0+ file anywhere if (safe_stricmp(psz_basename, ldlinux_c32) == 0) { has_ldlinux_c32 = TRUE; } // Check for an old incompatible c32 file anywhere for (i=0; i<NB_OLD_C32; i++) { is_old_c32[i] = FALSE; if ((safe_stricmp(psz_basename, old_c32_name[i]) == 0) && (i_file_length <= old_c32_threshold[i])) is_old_c32[i] = TRUE; } if (scan_only) { // Check for a "bootmgr(.efi)" file in root (psz_path = "") if (*psz_dirname == 0) { if (safe_strnicmp(psz_basename, bootmgr_efi_name, safe_strlen(bootmgr_efi_name)-5) == 0) { iso_report.has_bootmgr = TRUE; } if (safe_stricmp(psz_basename, bootmgr_efi_name) == 0) { iso_report.has_win7_efi = TRUE; } } // Check for ReactOS' setupldr.sys anywhere if ((iso_report.reactos_path[0] == 0) && (safe_stricmp(psz_basename, reactos_name) == 0)) safe_strcpy(iso_report.reactos_path, sizeof(iso_report.reactos_path), psz_fullpath); // Check for the EFI boot directory if (safe_stricmp(psz_dirname, efi_dirname) == 0) iso_report.has_efi = TRUE; // Check for PE (XP) specific files in "/i386" or "/minint" for (i=0; i<ARRAYSIZE(pe_dirname); i++) if (safe_stricmp(psz_dirname, pe_dirname[i]) == 0) for (j=0; j<ARRAYSIZE(pe_file); j++) if (safe_stricmp(psz_basename, pe_file[j]) == 0) iso_report.winpe |= (1<<i)<<(ARRAYSIZE(pe_dirname)*j); if (*is_syslinux_cfg) { // Maintain a list of all the isolinux/syslinux configs identified so far StrArrayAdd(&config_path, psz_fullpath); } if (safe_stricmp(psz_basename, isolinux_bin) == 0) { // Maintain a list of all the isolinux.bin files found StrArrayAdd(&isolinux_path, psz_fullpath); } for (i=0; i<NB_OLD_C32; i++) { if (is_old_c32[i]) iso_report.has_old_c32[i] = TRUE; } if (i_file_length >= FOUR_GIGABYTES) iso_report.has_4GB_file = TRUE; // Compute projected size needed total_blocks += i_file_length/UDF_BLOCKSIZE; // NB: ISO_BLOCKSIZE = UDF_BLOCKSIZE if ((i_file_length != 0) && (i_file_length%ISO_BLOCKSIZE == 0)) // total_blocks++; return TRUE; } // In case there's an ldlinux.sys on the ISO, prevent it from overwriting ours if ((*psz_dirname == 0) && (safe_strcmp(psz_basename, ldlinux_name) == 0)) { uprintf("skipping % file from ISO image\n", ldlinux_name); return TRUE; } return FALSE; }
int GSI_SOCKET_read_token(GSI_SOCKET *self, unsigned char **pbuffer, size_t *pbuffer_len) { int bytes_read; static unsigned char *saved_buffer = NULL; /* not thread safe! */ static int saved_buffer_len = 0; unsigned char *buffer; int return_status = GSI_SOCKET_ERROR; if (saved_buffer) { buffer = saved_buffer; bytes_read = saved_buffer_len; saved_buffer = NULL; saved_buffer_len = 0; } else { bytes_read = read_token(self->sock, (char **) &buffer, self->max_token_len); if (bytes_read == -1) { self->error_number = errno; GSI_SOCKET_set_error_string(self, "failed to read token"); goto error; } if (bytes_read == 0) { self->error_number = errno; GSI_SOCKET_set_error_string(self, "connection closed"); goto error; } if (self->gss_context != GSS_C_NO_CONTEXT) { /* Need to unwrap read data */ gss_buffer_desc unwrapped_buffer; gss_buffer_desc wrapped_buffer; int conf_state; gss_qop_t qop_state; wrapped_buffer.value = buffer; wrapped_buffer.length = bytes_read; self->major_status = gss_unwrap(&self->minor_status, self->gss_context, &wrapped_buffer, &unwrapped_buffer, &conf_state, &qop_state); free(buffer); if (self->major_status != GSS_S_COMPLETE) { goto error; } buffer = unwrapped_buffer.value; bytes_read = unwrapped_buffer.length; } } if (bytes_read == 0) { self->error_number = errno; GSI_SOCKET_set_error_string(self, "connection closed"); goto error; } /* HACK: We may have multiple tokens concatenated together here. Unfortunately, our protocol doesn't do a good job of message framing. Still, we can find the start/end of some messages by looking for the standard VERSION string at the start. */ if (strncmp((const char *)buffer, "VERSION", strlen("VERSION")) == 0) { size_t token_len = safe_strlen((const char *)buffer, bytes_read)+1; if (bytes_read > token_len) { /* Our buffer is bigger than one message. Just return the one message here and save the rest for later. */ char *old_buffer; old_buffer = (char *)buffer; saved_buffer_len = bytes_read - token_len; buffer = malloc(token_len); memcpy(buffer, old_buffer, token_len); saved_buffer = malloc(saved_buffer_len); memcpy(saved_buffer, old_buffer+token_len, saved_buffer_len); bytes_read = token_len; free(old_buffer); } } /* Success */ *pbuffer = buffer; *pbuffer_len = bytes_read; return_status = GSI_SOCKET_SUCCESS; /* fprintf(stderr, "\nread:\n%s\n", buffer); */ #if 0 if (buffer[bytes_read-1] == '\0') { myproxy_debug("read a null-terminated message"); } else { myproxy_debug("read a non-null-terminated message"); } #endif error: return return_status; }
/* * Returns the first drive letter for a volume located on the drive identified by DriveIndex, * as well as the drive type. This is used as base for the 2 function calls that follow. */ static BOOL _GetDriveLetterAndType(DWORD DriveIndex, char* drive_letter, UINT* drive_type) { DWORD size; BOOL r = FALSE; STORAGE_DEVICE_NUMBER_REDEF device_number = {0}; HANDLE hDrive = INVALID_HANDLE_VALUE; UINT _drive_type; char *drive, drives[26*4]; /* "D:\", "E:\", etc. */ char logical_drive[] = "\\\\.\\#:"; if (drive_letter != NULL) *drive_letter = ' '; if (drive_type != NULL) *drive_type = DRIVE_UNKNOWN; CheckDriveIndex(DriveIndex); size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: buffer too small (required %d vs %d)\n", size, sizeof(drives)); goto out; } r = TRUE; for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; *drive = (char)toupper((int)*drive); if (*drive < 'C') { continue; } /* IOCTL_STORAGE_GET_DEVICE_NUMBER's STORAGE_DEVICE_NUMBER.DeviceNumber is not unique! An HDD, a DVD and probably other drives can have the same value there => Use GetDriveType() to filter out unwanted devices. See https://github.com/pbatard/rufus/issues/32 for details. */ _drive_type = GetDriveTypeA(drive); if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED)) continue; safe_sprintf(logical_drive, sizeof(logical_drive), "\\\\.\\%c:", drive[0]); hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDrive == INVALID_HANDLE_VALUE) { uprintf("Warning: could not open drive %c: %s\n", drive[0], WindowsErrorString()); continue; } r = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &device_number, sizeof(device_number), &size, NULL) && (size > 0); safe_closehandle(hDrive); if (!r) { uprintf("Could not get device number for device %s: %s\n", logical_drive, WindowsErrorString()); } else if (device_number.DeviceNumber == DriveIndex) { if (drive_letter != NULL) *drive_letter = *drive; if (drive_type != NULL) *drive_type = _drive_type; break; } } out: return r; }
/* * Returns the drive letters for all volumes located on the drive identified by DriveIndex, * as well as the drive type. This is used as base for the 2 function calls that follow. */ static BOOL _GetDriveLettersAndType(DWORD DriveIndex, char* drive_letters, UINT* drive_type) { DWORD size; BOOL r = FALSE; HANDLE hDrive = INVALID_HANDLE_VALUE; UINT _drive_type; int i = 0, drive_number; char *drive, drives[26*4]; /* "D:\", "E:\", etc. */ char logical_drive[] = "\\\\.\\#:"; if (drive_letters != NULL) drive_letters[0] = 0; if (drive_type != NULL) *drive_type = DRIVE_UNKNOWN; CheckDriveIndex(DriveIndex); size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: buffer too small (required %d vs %d)\n", size, sizeof(drives)); goto out; } r = TRUE; // Required to detect drives that don't have volumes assigned for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; *drive = (char)toupper((int)*drive); if (*drive < 'C') { continue; } /* IOCTL_STORAGE_GET_DEVICE_NUMBER's STORAGE_DEVICE_NUMBER.DeviceNumber is not unique! An HDD, a DVD and probably other drives can have the same value there => Use GetDriveType() to filter out unwanted devices. See https://github.com/pbatard/rufus/issues/32 for details. */ _drive_type = GetDriveTypeA(drive); if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED)) continue; safe_sprintf(logical_drive, sizeof(logical_drive), "\\\\.\\%c:", drive[0]); hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDrive == INVALID_HANDLE_VALUE) { uprintf("Warning: could not open drive %c: %s\n", drive[0], WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, logical_drive); safe_closehandle(hDrive); if (drive_number == DriveIndex) { r = TRUE; if (drive_letters != NULL) drive_letters[i++] = *drive; // The drive type should be the same for all volumes, so we can overwrite if (drive_type != NULL) *drive_type = _drive_type; } } out: if (drive_letters != NULL) drive_letters[i] = 0; return r; }
/* * Download a file from an URL * Mostly taken from http://support.microsoft.com/kb/234913 * If hProgressDialog is not NULL, this function will send INIT and EXIT messages * to the dialog in question, with WPARAM being set to nonzero for EXIT on success * and also attempt to indicate progress using an IDC_PROGRESS control */ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog) { HWND hProgressBar = NULL; BOOL r = FALSE; LONG progress_style; DWORD dwFlags, dwSize, dwWritten, dwDownloaded, dwTotalSize; DWORD DownloadStatus; HANDLE hFile = INVALID_HANDLE_VALUE; const char* accept_types[] = {"*/*\0", NULL}; unsigned char buf[DOWNLOAD_BUFFER_SIZE]; char agent[64], hostname[64], urlpath[128], msg[MAX_PATH]; HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL; URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0, hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1}; size_t last_slash; int i; DownloadStatus = 404; if (hProgressDialog != NULL) { // Use the progress control provided, if any hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS); if (hProgressBar != NULL) { progress_style = GetWindowLong(hProgressBar, GWL_STYLE); SetWindowLong(hProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hProgressBar, PBM_SETPOS, 0, 0); } SendMessage(hProgressDialog, UM_DOWNLOAD_INIT, 0, 0); } if (file == NULL) goto out; for (last_slash = safe_strlen(file); last_slash != 0; last_slash--) { if ((file[last_slash] == '/') || (file[last_slash] == '\\')) { last_slash++; break; } } static_sprintf(msg, "Downloading %s: Connecting...", file); print_status(0, FALSE, msg); dprintf("Downloading %s from %s\n", file, url); if ( (!InternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts)) || (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL)) { dprintf("Unable to decode URL: %s\n", WinInetErrorString()); goto out; } hostname[sizeof(hostname)-1] = 0; // Open an Internet session for (i=5; (i>0) && (!InternetGetConnectedState(&dwFlags, 0)); i--) { Sleep(1000); } if (i <= 0) { // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong... SetLastError(ERROR_INTERNET_NOT_INITIALIZED); dprintf("Network is unavailable: %s\n", WinInetErrorString()); goto out; } static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)", application_version[0], application_version[1], application_version[2], nWindowsVersion>>4, nWindowsVersion&0x0F, is_x64()?"; WOW64":""); hSession = InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hSession == NULL) { dprintf("Could not open Internet session: %s\n", WinInetErrorString()); goto out; } hConnection = InternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL); if (hConnection == NULL) { dprintf("Could not connect to server %s:%d: %s\n", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString()); goto out; } hRequest = HttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS| INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK| ((UrlParts.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL); if (hRequest == NULL) { dprintf("Could not open URL %s: %s\n", url, WinInetErrorString()); goto out; } if (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0)) { dprintf("Unable to send request: %s\n", WinInetErrorString()); goto out; } // Get the file size dwSize = sizeof(DownloadStatus); HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL); if (DownloadStatus != 200) { error_code = ERROR_SEVERITY_ERROR|ERROR_INTERNET_ITEM_NOT_FOUND; dprintf("Unable to access file: %d\n", DownloadStatus); goto out; } dwSize = sizeof(dwTotalSize); if (!HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) { dprintf("Unable to retrieve file length: %s\n", WinInetErrorString()); goto out; } dprintf("File length: %d bytes\n", dwTotalSize); hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { dprintf("Unable to create file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } // Keep checking for data until there is nothing left. dwSize = 0; while (1) { if (IS_ERROR(error_code)) goto out; if (!InternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0)) break; dwSize += dwDownloaded; SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize)/(1.0f*dwTotalSize))), 0); static_sprintf(msg, "Downloading: %0.1f%%", (100.0f*dwSize)/(1.0f*dwTotalSize)); print_status(0, FALSE, msg); if (!WriteFile(hFile, buf, dwDownloaded, &dwWritten, NULL)) { dprintf("Error writing file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } else if (dwDownloaded != dwWritten) { dprintf("Error writing file '%s': Only %d/%d bytes written\n", dwWritten, dwDownloaded); goto out; } } if (dwSize != dwTotalSize) { dprintf("Could not download complete file - read: %d bytes, expected: %d bytes\n", dwSize, dwTotalSize); error_code = ERROR_SEVERITY_ERROR|ERROR_WRITE_FAULT; goto out; } else { r = TRUE; dprintf("Successfully downloaded '%s'\n", &file[last_slash]); } out: if (hProgressDialog != NULL) SendMessage(hProgressDialog, UM_DOWNLOAD_EXIT, (WPARAM)r, 0); if (hFile != INVALID_HANDLE_VALUE) { // Force a flush - May help with the PKI API trying to process downloaded updates too early... FlushFileBuffers(hFile); CloseHandle(hFile); } if (!r) { if (file != NULL) _unlinkU(file); print_status(0, FALSE, "Failed to download file."); SetLastError(error_code); MessageBoxU(hMainDialog, WinInetErrorString(), "File download", MB_OK|MB_ICONERROR); } if (hRequest) InternetCloseHandle(hRequest); if (hConnection) InternetCloseHandle(hConnection); if (hSession) InternetCloseHandle(hSession); return r?dwSize:0; }
/* * Return the UTF8 path of a file selected through a load or save dialog * All string parameters are UTF-8 * IMPORTANT NOTE: Remember that you need to call CoInitializeEx() for * *EACH* thread you invoke FileDialog from, as GetDisplayName() will * return error 0x8001010E otherwise. */ char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options) { DWORD tmp; OPENFILENAMEA ofn; char selected_name[MAX_PATH]; char *ext_string = NULL, *all_files = NULL; size_t i, j, ext_strlen; BOOL r; char* filepath = NULL; HRESULT hr = FALSE; IFileDialog *pfd = NULL; IShellItem *psiResult; COMDLG_FILTERSPEC* filter_spec = NULL; wchar_t *wpath = NULL, *wfilename = NULL; IShellItem *si_path = NULL; // Automatically freed if ((ext == NULL) || (ext->count == 0) || (ext->extension == NULL) || (ext->description == NULL)) return NULL; dialog_showing++; filter_spec = (COMDLG_FILTERSPEC*)calloc(ext->count + 1, sizeof(COMDLG_FILTERSPEC)); if (filter_spec != NULL) { // Setup the file extension filter table for (i = 0; i < ext->count; i++) { filter_spec[i].pszSpec = utf8_to_wchar(ext->extension[i]); filter_spec[i].pszName = utf8_to_wchar(ext->description[i]); } filter_spec[i].pszSpec = L"*.*"; filter_spec[i].pszName = L"All files"; hr = CoCreateInstance(save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC, &IID_IFileDialog, (LPVOID)&pfd); if (FAILED(hr)) { SetLastError(hr); dprintf("CoCreateInstance for FileOpenDialog failed: %s\n", WindowsErrorString()); pfd = NULL; // Just in case goto fallback; } // Set the file extension filters pfd->lpVtbl->SetFileTypes(pfd, (UINT)ext->count + 1, filter_spec); // Set the default directory wpath = utf8_to_wchar(path); hr = SHCreateItemFromParsingName(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path); if (SUCCEEDED(hr)) { pfd->lpVtbl->SetFolder(pfd, si_path); } safe_free(wpath); // Set the default filename wfilename = utf8_to_wchar((ext->filename == NULL) ? "" : ext->filename); if (wfilename != NULL) { pfd->lpVtbl->SetFileName(pfd, wfilename); } // Display the dialog hr = pfd->lpVtbl->Show(pfd, hMainDialog); // Cleanup safe_free(wfilename); for (i = 0; i < ext->count; i++) { safe_free(filter_spec[i].pszSpec); safe_free(filter_spec[i].pszName); } safe_free(filter_spec); if (SUCCEEDED(hr)) { // Obtain the result of the user's interaction with the dialog. hr = pfd->lpVtbl->GetResult(pfd, &psiResult); if (SUCCEEDED(hr)) { hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath); if (SUCCEEDED(hr)) { filepath = wchar_to_utf8(wpath); CoTaskMemFree(wpath); } else { SetLastError(hr); dprintf("Unable to access file path: %s\n", WindowsErrorString()); } psiResult->lpVtbl->Release(psiResult); } } else if ((hr & 0xFFFF) != ERROR_CANCELLED) { // If it's not a user cancel, assume the dialog didn't show and fallback SetLastError(hr); dprintf("Could not show FileOpenDialog: %s\n", WindowsErrorString()); goto fallback; } pfd->lpVtbl->Release(pfd); dialog_showing--; return filepath; } fallback: safe_free(filter_spec); if (pfd != NULL) { pfd->lpVtbl->Release(pfd); } memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hMainDialog; // Selected File name static_sprintf(selected_name, "%s", (ext->filename == NULL) ? "" : ext->filename); ofn.lpstrFile = selected_name; ofn.nMaxFile = MAX_PATH; // Set the file extension filters all_files = "All files"; ext_strlen = 0; for (i = 0; i<ext->count; i++) { ext_strlen += safe_strlen(ext->description[i]) + 2 * safe_strlen(ext->extension[i]) + sizeof(" ()\r\r"); } ext_strlen += safe_strlen(all_files) + sizeof(" (*.*)\r*.*\r"); ext_string = (char*)malloc(ext_strlen + 1); if (ext_string == NULL) return NULL; ext_string[0] = 0; for (i = 0, j = 0; i<ext->count; i++) { j += _snprintf(&ext_string[j], ext_strlen - j, "%s (%s)\r%s\r", ext->description[i], ext->extension[i], ext->extension[i]); } j = _snprintf(&ext_string[j], ext_strlen - j, "%s (*.*)\r*.*\r", all_files); // Microsoft could really have picked a better delimiter! for (i = 0; i<ext_strlen; i++) { // Since the VS Code Analysis tool is dumb... #if defined(_MSC_VER) #pragma warning(suppress: 6385) #endif if (ext_string[i] == '\r') { #if defined(_MSC_VER) #pragma warning(suppress: 6386) #endif ext_string[i] = 0; } } ofn.lpstrFilter = ext_string; ofn.nFilterIndex = 1; ofn.lpstrInitialDir = path; ofn.Flags = OFN_OVERWRITEPROMPT | options; // Show Dialog if (save) { r = GetSaveFileNameU(&ofn); } else { r = GetOpenFileNameU(&ofn); } if (r) { filepath = safe_strdup(selected_name); } else { tmp = CommDlgExtendedError(); if (tmp != 0) { dprintf("Could not select file for %s. Error %X\n", save ? "save" : "open", tmp); } } safe_free(ext_string); dialog_showing--; return filepath; }
/* * Download a file from an URL * Mostly taken from http://support.microsoft.com/kb/234913 * If hProgressDialog is not NULL, this function will send INIT and EXIT messages * to the dialog in question, with WPARAM being set to nonzero for EXIT on success * and also attempt to indicate progress using an IDC_PROGRESS control */ DWORD DownloadFile(const char* url, const char* file, HWND hProgressDialog) { HWND hProgressBar = NULL; BOOL r = FALSE; DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus; FILE* fd = NULL; LONG progress_style; const char* accept_types[] = {"*/*\0", NULL}; unsigned char buf[DOWNLOAD_BUFFER_SIZE]; char agent[64], hostname[64], urlpath[128]; HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL; URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0, hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1}; size_t last_slash; int i; if (hProgressDialog != NULL) { // Use the progress control provided, if any hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS); if (hProgressBar != NULL) { progress_style = GetWindowLong(hProgressBar, GWL_STYLE); SetWindowLong(hProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hProgressBar, PBM_SETPOS, 0, 0); } SendMessage(hProgressDialog, UM_ISO_INIT, 0, 0); } for (last_slash = safe_strlen(file); last_slash != 0; last_slash--) { if ((file[last_slash] == '/') || (file[last_slash] == '\\')) { last_slash++; break; } } PrintStatus(0, FALSE, MSG_240, &file[last_slash]); uprintf("Downloading '%s' from %s\n", &file[last_slash], url); if (!InternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts)) { uprintf("Unable to decode URL: %s\n", WinInetErrorString()); goto out; } hostname[sizeof(hostname)-1] = 0; // Open an Internet session for (i=5; (i>0) && (!InternetGetConnectedState(&dwFlags, 0)); i--) { Sleep(1000); } if (i <= 0) { // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong... SetLastError(ERROR_INTERNET_NOT_INITIALIZED); uprintf("Network is unavailable: %s\n", WinInetErrorString()); goto out; } _snprintf(agent, ARRAYSIZE(agent), APPLICATION_NAME "/%d.%d.%d.%d", rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]); hSession = InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hSession == NULL) { uprintf("Could not open Internet session: %s\n", WinInetErrorString()); goto out; } hConnection = InternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL); if (hConnection == NULL) { uprintf("Could not connect to server %s:%d: %s\n", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString()); goto out; } hRequest = HttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types, INTERNET_FLAG_HYPERLINK|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|INTERNET_FLAG_NO_COOKIES| INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)NULL); if (hRequest == NULL) { uprintf("Could not open URL %s: %s\n", url, WinInetErrorString()); goto out; } if (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0)) { uprintf("Unable to send request: %s\n", WinInetErrorString()); goto out; } // Get the file size dwSize = sizeof(dwStatus); dwStatus = 404; HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwStatus, &dwSize, NULL); if (dwStatus != 200) { error_code = ERROR_INTERNET_ITEM_NOT_FOUND; uprintf("Unable to access file: Server status %d\n", dwStatus); goto out; } dwSize = sizeof(dwTotalSize); if (!HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) { uprintf("Unable to retrieve file length: %s\n", WinInetErrorString()); goto out; } uprintf("File length: %d bytes\n", dwTotalSize); fd = fopenU(file, "wb"); if (fd == NULL) { uprintf("Unable to create file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } // Keep checking for data until there is nothing left. dwSize = 0; while (1) { if (IS_ERROR(FormatStatus)) goto out; if (!InternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0)) break; dwSize += dwDownloaded; SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize)/(1.0f*dwTotalSize))), 0); PrintStatus(0, FALSE, MSG_241, (100.0f*dwSize)/(1.0f*dwTotalSize)); if (fwrite(buf, 1, dwDownloaded, fd) != dwDownloaded) { uprintf("Error writing file '%s': %s\n", &file[last_slash], WinInetErrorString()); goto out; } } if (dwSize != dwTotalSize) { uprintf("Could not download complete file - read: %d bytes, expected: %d bytes\n", dwSize, dwTotalSize); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT; goto out; } else { r = TRUE; uprintf("Successfully downloaded '%s'\n", &file[last_slash]); } out: if (hProgressDialog != NULL) SendMessage(hProgressDialog, UM_ISO_EXIT, (WPARAM)r, 0); if (fd != NULL) fclose(fd); if (!r) { _unlink(file); PrintStatus(0, FALSE, MSG_242); SetLastError(error_code); MessageBoxU(hMainDialog, IS_ERROR(FormatStatus)?StrError(FormatStatus, FALSE):WinInetErrorString(), lmprintf(MSG_044), MB_OK|MB_ICONERROR|MB_IS_RTL); } if (hRequest) InternetCloseHandle(hRequest); if (hConnection) InternetCloseHandle(hConnection); if (hSession) InternetCloseHandle(hSession); return r?dwSize:0; }
static __inline void ToUpper(char* str) { size_t i; for (i = 0; i < safe_strlen(str); i++) str[i] = (char)toupper(str[i]); }
/* * Return the first GUID volume name for the associated drive or NULL if not found * See http://msdn.microsoft.com/en-us/library/cc542456.aspx * The returned string is allocated and must be freed */ char* GetLogicalName(DWORD DriveIndex, BOOL bKeepTrailingBackslash, BOOL bSilent) { BOOL success = FALSE; char volume_name[MAX_PATH]; HANDLE hDrive = INVALID_HANDLE_VALUE, hVolume = INVALID_HANDLE_VALUE; size_t len; char path[MAX_PATH]; VOLUME_DISK_EXTENTS DiskExtents; DWORD size; UINT drive_type; int i, j; static const char* ignore_device[] = { "\\Device\\CdRom", "\\Device\\Floppy" }; static const char* volume_start = "\\\\?\\"; CheckDriveIndex(DriveIndex); for (i=0; hDrive == INVALID_HANDLE_VALUE; i++) { if (i == 0) { hVolume = FindFirstVolumeA(volume_name, sizeof(volume_name)); if (hVolume == INVALID_HANDLE_VALUE) { suprintf("Could not access first GUID volume: %s\n", WindowsErrorString()); goto out; } } else { if (!FindNextVolumeA(hVolume, volume_name, sizeof(volume_name))) { if (GetLastError() != ERROR_NO_MORE_FILES) { suprintf("Could not access next GUID volume: %s\n", WindowsErrorString()); } goto out; } } // Sanity checks len = safe_strlen(volume_name); if ((len <= 1) || (safe_strnicmp(volume_name, volume_start, 4) != 0) || (volume_name[len-1] != '\\')) { suprintf("'%s' is not a GUID volume name\n", volume_name); continue; } drive_type = GetDriveTypeA(volume_name); if ((drive_type != DRIVE_REMOVABLE) && (drive_type != DRIVE_FIXED)) continue; volume_name[len-1] = 0; if (QueryDosDeviceA(&volume_name[4], path, sizeof(path)) == 0) { suprintf("Failed to get device path for GUID volume '%s': %s\n", volume_name, WindowsErrorString()); continue; } for (j=0; (j<ARRAYSIZE(ignore_device)) && (_strnicmp(path, ignore_device[j], safe_strlen(ignore_device[j])) != 0); j++); if (j < ARRAYSIZE(ignore_device)) { suprintf("Skipping GUID volume for '%s'\n", path); continue; } // If we can't have FILE_SHARE_WRITE, forget it hDrive = CreateFileA(volume_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { suprintf("Could not open GUID volume '%s': %s\n", volume_name, WindowsErrorString()); continue; } if ((!DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &DiskExtents, sizeof(DiskExtents), &size, NULL)) || (size <= 0)) { suprintf("Could not get Disk Extents: %s\n", WindowsErrorString()); safe_closehandle(hDrive); continue; } safe_closehandle(hDrive); if ((DiskExtents.NumberOfDiskExtents >= 1) && (DiskExtents.Extents[0].DiskNumber == DriveIndex)) { if (bKeepTrailingBackslash) volume_name[len-1] = '\\'; success = TRUE; break; } } out: if (hVolume != INVALID_HANDLE_VALUE) FindVolumeClose(hVolume); return (success)?safe_strdup(volume_name):NULL; }
/* * Returns the drive letters for all volumes located on the drive identified by DriveIndex, * as well as the drive type. This is used as base for the 2 function calls that follow. */ static BOOL _GetDriveLettersAndType(DWORD DriveIndex, char* drive_letters, UINT* drive_type) { DWORD size; BOOL r = FALSE; HANDLE hDrive = INVALID_HANDLE_VALUE; UINT _drive_type; int i = 0, drive_number; char *drive, drives[26*4 + 1]; /* "D:\", "E:\", etc., plus one NUL */ char logical_drive[] = "\\\\.\\#:"; if (drive_letters != NULL) drive_letters[0] = 0; if (drive_type != NULL) *drive_type = DRIVE_UNKNOWN; CheckDriveIndex(DriveIndex); // This call is weird... The buffer needs to have an extra NUL, but you're // supposed to provide the size without the extra NUL. And the returned size // does not include the NUL either *EXCEPT* if your buffer is too small... // But then again, this doesn't hold true if you have a 105 byte buffer and // pass a 4*26=104 size, as the the call will return 105 (i.e. *FAILURE*) // instead of 104 as it should => screw Microsoft: We'll include the NUL // always, as each drive string is at least 4 chars long anyway. size = GetLogicalDriveStringsA(sizeof(drives), drives); if (size == 0) { uprintf("GetLogicalDriveStrings failed: %s\n", WindowsErrorString()); goto out; } if (size > sizeof(drives)) { uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)\n", size, sizeof(drives)); goto out; } r = TRUE; // Required to detect drives that don't have volumes assigned for (drive = drives ;*drive; drive += safe_strlen(drive)+1) { if (!isalpha(*drive)) continue; *drive = (char)toupper((int)*drive); if (*drive < 'C') { continue; } // IOCTL_STORAGE_GET_DEVICE_NUMBER's STORAGE_DEVICE_NUMBER.DeviceNumber is // not unique! An HDD, a DVD and probably other drives can have the same // value there => Use GetDriveType() to filter out unwanted devices. // See https://github.com/pbatard/rufus/issues/32#issuecomment-3785956 _drive_type = GetDriveTypeA(drive); if ((_drive_type != DRIVE_REMOVABLE) && (_drive_type != DRIVE_FIXED)) continue; safe_sprintf(logical_drive, sizeof(logical_drive), "\\\\.\\%c:", drive[0]); hDrive = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { // uprintf("Warning: could not open drive %c: %s\n", drive[0], WindowsErrorString()); continue; } drive_number = GetDriveNumber(hDrive, logical_drive); safe_closehandle(hDrive); if (drive_number == DriveIndex) { r = TRUE; if (drive_letters != NULL) drive_letters[i++] = *drive; // The drive type should be the same for all volumes, so we can overwrite if (drive_type != NULL) *drive_type = _drive_type; } } out: if (drive_letters != NULL) drive_letters[i] = 0; return r; }
/* * Background thread to check for updates */ static DWORD WINAPI CheckForUpdatesThread(LPVOID param) { BOOL releases_only, found_new_version = FALSE; int status = 0; const char* server_url = RUFUS_URL "/"; int i, j, k, verbose = 0, verpos[4]; static const char* archname[] = {"win_x86", "win_x64"}; static const char* channel[] = {"release", "beta"}; // release channel const char* accept_types[] = {"*/*\0", NULL}; DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus; char* buf = NULL; char agent[64], hostname[64], urlpath[128], mime[32]; OSVERSIONINFOA os_version = {sizeof(OSVERSIONINFOA), 0, 0, 0, 0, ""}; HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL; URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0, hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1}; SYSTEMTIME ServerTime, LocalTime; FILETIME FileTime; int64_t local_time = 0, reg_time, server_time, update_interval; update_check_in_progress = TRUE; verbose = ReadRegistryKey32(REGKEY_HKCU, REGKEY_VERBOSE_UPDATES); // Without this the FileDialog will produce error 0x8001010E when compiled for Vista or later IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); // Unless the update was forced, wait a while before performing the update check if (!force_update_check) { // It would of course be a lot nicer to use a timer and wake the thread, but my // development time is limited and this is FASTER to implement. do { for (i=0; (i<30) && (!force_update_check); i++) Sleep(500); } while ((!force_update_check) && ((iso_op_in_progress || format_op_in_progress || (dialog_showing>0)))); if (!force_update_check) { if ((ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL) == -1)) { vuprintf("Check for updates disabled, as per registry settings.\n"); goto out; } reg_time = ReadRegistryKey64(REGKEY_HKCU, REGKEY_LAST_UPDATE); update_interval = (int64_t)ReadRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL); if (update_interval == 0) { WriteRegistryKey32(REGKEY_HKCU, REGKEY_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL); update_interval = DEFAULT_UPDATE_INTERVAL; } GetSystemTime(&LocalTime); if (!SystemTimeToFileTime(&LocalTime, &FileTime)) goto out; local_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000; vvuprintf("Local time: %" PRId64 "\n", local_time); if (local_time < reg_time + update_interval) { vuprintf("Next update check in %" PRId64 " seconds.\n", reg_time + update_interval - local_time); goto out; } } } PrintStatus(3000, TRUE, MSG_243); status++; // 1 if (!GetVersionExA(&os_version)) { uprintf("Could not read Windows version - Check for updates cancelled.\n"); goto out; } if ((!InternetCrackUrlA(server_url, (DWORD)safe_strlen(server_url), 0, &UrlParts)) || (!InternetGetConnectedState(&dwFlags, 0))) goto out; hostname[sizeof(hostname)-1] = 0; safe_sprintf(agent, ARRAYSIZE(agent), APPLICATION_NAME "/%d.%d.%d.%d", rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]); hSession = InternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (hSession == NULL) goto out; hConnection = InternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL); if (hConnection == NULL) goto out; status++; // 2 releases_only = !GetRegistryKeyBool(REGKEY_HKCU, REGKEY_INCLUDE_BETAS); for (k=0; (k<(releases_only?1:(int)ARRAYSIZE(channel))) && (!found_new_version); k++) { uprintf("Checking %s channel...\n", channel[k]); // At this stage we can query the server for various update version files. // We first try to lookup for "<appname>_<os_arch>_<os_version_major>_<os_version_minor>.ver" // and then remove each each of the <os_> components until we find our match. For instance, we may first // look for rufus_win_x64_6.2.ver (Win8 x64) but only get a match for rufus_win_x64_6.ver (Vista x64 or later) // This allows sunsetting OS versions (eg XP) or providing different downloads for different archs/groups. safe_sprintf(urlpath, sizeof(urlpath), "%s%s%s_%s_%d.%d.ver", APPLICATION_NAME, (k==0)?"":"_", (k==0)?"":channel[k], archname[is_x64()?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion); vuprintf("Base update check: %s\n", urlpath); for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) { if ((urlpath[j] == '.') || (urlpath[j] == '_')) { verpos[i++] = j; } } if (i != ARRAYSIZE(verpos)) { uprintf("Broken code in CheckForUpdatesThread()!\n"); goto out; } UrlParts.lpszUrlPath = urlpath; UrlParts.dwUrlPathLength = sizeof(urlpath); for (i=0; i<ARRAYSIZE(verpos); i++) { vvuprintf("Trying %s\n", UrlParts.lpszUrlPath); hRequest = HttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types, INTERNET_FLAG_HYPERLINK|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|INTERNET_FLAG_NO_COOKIES| INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)NULL); if ((hRequest == NULL) || (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0))) goto out; // Ensure that we get a text file dwSize = sizeof(dwStatus); dwStatus = 404; HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwStatus, &dwSize, NULL); if (dwStatus == 200) break; InternetCloseHandle(hRequest); hRequest = NULL; safe_strcpy(&urlpath[verpos[i]], 5, ".ver"); } if (dwStatus != 200) { vuprintf("Could not find a %s version file on server %s", channel[k], server_url); if ((releases_only) || (k+1 >= ARRAYSIZE(channel))) goto out; continue; } vuprintf("Found match for %s on server %s", urlpath, server_url); dwSize = sizeof(mime); HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_TYPE, (LPVOID)&mime, &dwSize, NULL); if (strcmp(mime, "text/plain") != 0) goto out; // We also get a date from Apache, which we'll use to avoid out of sync check, // in case some set their clock way into the future and back. // On the other hand, if local clock is set way back in the past, we will never check. dwSize = sizeof(ServerTime); // If we can't get a date we can trust, don't bother... if ( (!HttpQueryInfoA(hRequest, HTTP_QUERY_DATE|HTTP_QUERY_FLAG_SYSTEMTIME, (LPVOID)&ServerTime, &dwSize, NULL)) || (!SystemTimeToFileTime(&ServerTime, &FileTime)) ) goto out; server_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000; vvuprintf("Server time: %" PRId64 "\n", server_time); // Always store the server response time - the only clock we trust! WriteRegistryKey64(REGKEY_HKCU, REGKEY_LAST_UPDATE, server_time); // Might as well let the user know if (!force_update_check) { if ((local_time > server_time + 600) || (local_time < server_time - 600)) { uprintf("IMPORTANT: Your local clock is more than 10 minutes in the %s. Unless you fix this, " APPLICATION_NAME " may not be able to check for updates...", (local_time > server_time + 600)?"future":"past"); } } dwSize = sizeof(dwTotalSize); if (!HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL)) goto out; safe_free(buf); // Make sure the file is NUL terminated buf = (char*)calloc(dwTotalSize+1, 1); if (buf == NULL) goto out; // This is a version file - we should be able to gulp it down in one go if (!InternetReadFile(hRequest, buf, dwTotalSize, &dwDownloaded) || (dwDownloaded != dwTotalSize)) goto out; status++; vuprintf("Successfully downloaded version file (%d bytes)\n", dwTotalSize); parse_update(buf, dwTotalSize+1); vuprintf("UPDATE DATA:\n"); vuprintf(" version: %d.%d.%d.%d (%s)\n", update.version[0], update.version[1], update.version[2], update.version[3], channel[k]); vuprintf(" platform_min: %d.%d\n", update.platform_min[0], update.platform_min[1]); vuprintf(" url: %s\n", update.download_url); found_new_version = ((to_uint64_t(update.version) > to_uint64_t(rufus_version)) || (force_update)) && ( (os_version.dwMajorVersion > update.platform_min[0]) || ( (os_version.dwMajorVersion == update.platform_min[0]) && (os_version.dwMinorVersion >= update.platform_min[1])) ); uprintf("N%sew %s version found%c\n", found_new_version?"":"o n", channel[k], found_new_version?'!':'.'); } out: safe_free(buf); if (hRequest) InternetCloseHandle(hRequest); if (hConnection) InternetCloseHandle(hConnection); if (hSession) InternetCloseHandle(hSession); switch(status) { case 1: PrintStatus(3000, TRUE, MSG_244); break; case 2: PrintStatus(3000, TRUE, MSG_245); break; case 3: case 4: PrintStatus(3000, FALSE, found_new_version?MSG_246:MSG_247); default: break; } // Start the new download after cleanup if (found_new_version) { // User may have started an operation while we were checking while ((!force_update_check) && (iso_op_in_progress || format_op_in_progress || (dialog_showing>0))) { Sleep(15000); } DownloadNewVersion(); } force_update_check = FALSE; update_check_in_progress = FALSE; ExitThread(0); }
BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) { size_t i, k, size; int j; uint16_t sl_version; FILE* fd; int r = 1; iso9660_t* p_iso = NULL; udf_t* p_udf = NULL; udf_dirent_t* p_udf_root; LONG progress_style; char *tmp, *buf; char path[MAX_PATH]; const char* basedir[] = { "i386", "minint" }; const char* tmp_sif = ".\\txtsetup.sif~"; const char ISOLINUX[] = { 'I', 'S', 'O', 'L', 'I', 'N', 'U', 'X', ' ' }; iso_extension_mask_t iso_extension_mask = ISO_EXTENSION_ALL; if ((src_iso == NULL) || (dest_dir == NULL)) return FALSE; scan_only = scan; cdio_log_set_handler(log_handler); psz_extract_dir = dest_dir; progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE); if (scan_only) { total_blocks = 0; memset(&iso_report, 0, sizeof(iso_report)); has_ldlinux_c32 = FALSE; // String array of all isolinux/syslinux locations StrArrayCreate(&config_path, 8); StrArrayCreate(&isolinux_path, 8); // Change the Window title and static text SetWindowTextU(hISOProgressDlg, lmprintf(MSG_202)); SetWindowTextU(hISOFileName, lmprintf(MSG_202)); // Change progress style to marquee for scanning SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style | PBS_MARQUEE); SendMessage(hISOProgressBar, PBM_SETMARQUEE, TRUE, 0); } else { uprintf("Extracting files...\n"); IGNORE_RETVAL(_chdirU(app_dir)); SetWindowTextU(hISOProgressDlg, lmprintf(MSG_231)); if (total_blocks == 0) { uprintf("Error: ISO has not been properly scanned.\n"); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_SCAN); goto out; } nb_blocks = 0; iso_blocking_status = 0; SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); } SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0); /* First try to open as UDF - fallback to ISO if it failed */ p_udf = udf_open(src_iso); if (p_udf == NULL) goto try_iso; uprintf("Disc image is an UDF image\n"); p_udf_root = udf_get_root(p_udf, true, 0); if (p_udf_root == NULL) { uprintf("Couldn't locate UDF root directory\n"); goto out; } if (scan_only) { if (udf_get_logical_volume_id(p_udf, iso_report.label, sizeof(iso_report.label)) <= 0) iso_report.label[0] = 0; } r = udf_extract_files(p_udf, p_udf_root, ""); goto out; try_iso: // Perform our first scan with Joliet disabled (if Rock Ridge is enabled), so that we can find if // there exists a Rock Ridge file with a name > 64 chars or if there are symlinks. If that is the // case then we also disable Joliet during the extract phase. if ((!enable_joliet) || (enable_rockridge && (scan_only || iso_report.has_long_filename || iso_report.has_symlinks))) { iso_extension_mask &= ~ISO_EXTENSION_JOLIET; } if (!enable_rockridge) { iso_extension_mask &= ~ISO_EXTENSION_ROCK_RIDGE; } p_iso = iso9660_open_ext(src_iso, iso_extension_mask); if (p_iso == NULL) { uprintf("Unable to open '%s' as an ISO image.\n", src_iso); r = 1; goto out; } uprintf("Disc image is an ISO9660 image\n"); i_joliet_level = iso9660_ifs_get_joliet_level(p_iso); if (scan_only) { if (iso9660_ifs_get_volume_id(p_iso, &tmp)) { safe_strcpy(iso_report.label, sizeof(iso_report.label), tmp); safe_free(tmp); } else iso_report.label[0] = 0; } else { if (iso_extension_mask & (ISO_EXTENSION_JOLIET|ISO_EXTENSION_ROCK_RIDGE)) uprintf("This image will be extracted using %s extensions (if present)", (iso_extension_mask & ISO_EXTENSION_JOLIET)?"Joliet":"Rock Ridge"); else uprintf("This image will not be extracted using any ISO extensions"); } r = iso_extract_files(p_iso, ""); out: iso_blocking_status = -1; if (scan_only) { // Remove trailing spaces from the label for (j=(int)safe_strlen(iso_report.label)-1; ((j>=0)&&(isspaceU(iso_report.label[j]))); j--) iso_report.label[j] = 0; // We use the fact that UDF_BLOCKSIZE and ISO_BLOCKSIZE are the same here iso_report.projected_size = total_blocks * ISO_BLOCKSIZE; // We will link the existing isolinux.cfg from a syslinux.cfg we create // If multiple config files exist, choose the one with the shortest path // (so that a '/syslinux.cfg' is preferred over a '/isolinux/isolinux.cfg') if (!IsStrArrayEmpty(config_path)) { safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[0]); for (i=1; i<config_path.Index; i++) { if (safe_strlen(iso_report.cfg_path) > safe_strlen(config_path.String[i])) safe_strcpy(iso_report.cfg_path, sizeof(iso_report.cfg_path), config_path.String[i]); } uprintf("Will use %s for Syslinux\n", iso_report.cfg_path); // Extract all of the isolinux.bin files we found to identify their versions for (i=0; i<isolinux_path.Index; i++) { size = (size_t)ExtractISOFile(src_iso, isolinux_path.String[i], dot_isolinux_bin); if (size == 0) { uprintf("Could not access %s\n", isolinux_path.String[i]); } else { buf = (char*)calloc(size, 1); if (buf == NULL) break; fd = fopen(dot_isolinux_bin, "rb"); if (fd == NULL) { free(buf); continue; } fread(buf, 1, size, fd); fclose(fd); for (k=0; k<size-16; k++) { if (memcmp(&buf[k], ISOLINUX, sizeof(ISOLINUX)) == 0) { k += sizeof(ISOLINUX); sl_version = (((uint8_t)strtoul(&buf[k], &tmp, 10))<<8) + (uint8_t)strtoul(&tmp[1], NULL, 10); if (iso_report.sl_version == 0) { iso_report.sl_version = sl_version; j = (int)i; } else if (iso_report.sl_version != sl_version) { uprintf("Found conflicting %s versions:\n '%s' (%d.%02d) vs '%s' (%d.%02d)\n", isolinux_bin, isolinux_path.String[j], SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version), isolinux_path.String[i], SL_MAJOR(sl_version), SL_MINOR(sl_version)); } break; } } free(buf); _unlink(dot_isolinux_bin); } } if (iso_report.sl_version != 0) { static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Detected Isolinux version: %s (from '%s')", iso_report.sl_version_str, isolinux_path.String[j]); if ( (has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) < 5)) || (!has_ldlinux_c32 && (SL_MAJOR(iso_report.sl_version) >= 5)) ) uprintf("Warning: Conflict between Isolinux version and the presence of ldlinux.c32...\n"); } else { // Couldn't find a version from isolinux.bin. Force set to the versions we embed iso_report.sl_version = embedded_sl_version[has_ldlinux_c32?1:0]; static_sprintf(iso_report.sl_version_str, "%d.%02d", SL_MAJOR(iso_report.sl_version), SL_MINOR(iso_report.sl_version)); uprintf("Warning: Could not detect Isolinux version - Forcing to %s (embedded)", iso_report.sl_version_str); } } if (IS_WINPE(iso_report.winpe)) { // In case we have a WinPE 1.x based iso, we extract and parse txtsetup.sif // during scan, to see if /minint was provided for OsLoadOptions, as it decides // whether we should use 0x80 or 0x81 as the disk ID in the MBR safe_sprintf(path, sizeof(path), "/%s/txtsetup.sif", basedir[((iso_report.winpe&WINPE_I386) == WINPE_I386)?0:1]); ExtractISOFile(src_iso, path, tmp_sif); tmp = get_token_data_file("OsLoadOptions", tmp_sif); if (tmp != NULL) { for (i=0; i<strlen(tmp); i++) tmp[i] = (char)tolower(tmp[i]); uprintf("Checking txtsetup.sif:\n OsLoadOptions = %s\n", tmp); iso_report.uses_minint = (strstr(tmp, "/minint") != NULL); } _unlink(tmp_sif); safe_free(tmp); } StrArrayDestroy(&config_path); StrArrayDestroy(&isolinux_path); } else if (HAS_SYSLINUX(iso_report)) { safe_sprintf(path, sizeof(path), "%s\\syslinux.cfg", dest_dir); // Create a /syslinux.cfg (if none exists) that points to the existing isolinux cfg fd = fopen(path, "r"); if (fd == NULL) { fd = fopen(path, "w"); // No "/syslinux.cfg" => create a new one if (fd == NULL) { uprintf("Unable to create %s - booting from USB will not work\n", path); r = 1; } else { fprintf(fd, "DEFAULT loadconfig\n\nLABEL loadconfig\n CONFIG %s\n", iso_report.cfg_path); for (i=safe_strlen(iso_report.cfg_path); (i>0)&&(iso_report.cfg_path[i]!='/'); i--); if (i>0) { iso_report.cfg_path[i] = 0; fprintf(fd, " APPEND %s/\n", iso_report.cfg_path); iso_report.cfg_path[i] = '/'; } uprintf("Created: %s\n", path); } } if (fd != NULL) fclose(fd); } SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); if (p_iso != NULL) iso9660_close(p_iso); if (p_udf != NULL) udf_close(p_udf); if ((r != 0) && (FormatStatus == 0)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR((scan_only?ERROR_ISO_SCAN:ERROR_ISO_EXTRACT)); return (r == 0); }