void version_debug(const gchar *tag, GTree *versions) { if (!GRID_TRACE_ENABLED()) return; (void) tag; gchar *s = version_dump(versions); GRID_TRACE("%s %s (%s)", tag, s, __FUNCTION__); g_free(s); }
/** * Check version of servent, and if it's a gtk-gnutella more recent than we * are, record that fact and change the status bar. * * The `addr' is being passed solely for the tok_version_valid() call. * * @returns TRUE if we properly checked the version, FALSE if we got something * looking as gtk-gnutella but which failed the token-based sanity checks. */ bool version_check(const char *str, const char *token, const host_addr_t addr) { version_t their_version; version_ext_t their_version_ext; version_t *target_version; int cmp; const char *version; const char *end; bool extended; if (!version_parse(str, &their_version, &end)) return TRUE; /* Not gtk-gnutella, or unparseable */ /* * Check for extended version information (git commit ID, dirty status). */ ZERO(&their_version_ext); extended = version_ext_parse(end, &their_version_ext); if (!extended) { /* Structure could have been partially filled */ ZERO(&their_version_ext); } /* * Is their version a development one, or a release? */ if (their_version.tag == 'u') target_version = &last_dev_version; else target_version = &last_rel_version; cmp = version_cmp(target_version, &their_version); if (GNET_PROPERTY(version_debug) > 1) version_dump(str, &their_version, cmp == 0 ? "=" : cmp > 0 ? "-" : "+"); /* * Check timestamp. */ version_stamp(str, &their_version); their_version_ext.version = their_version; /* Struct copy */ if (GNET_PROPERTY(version_debug) > 3) g_debug("VERSION time=%d", (int) their_version.timestamp); /* * If version claims something older than TOKEN_START_DATE, then * there must be a token present. */ if (delta_time(their_version.timestamp, 0) >= TOKEN_START_DATE) { tok_error_t error; if (token == NULL) { if (GNET_PROPERTY(version_debug)) { g_debug("got GTKG vendor string \"%s\" without token!", str); } return FALSE; /* Can't be correct */ } error = tok_version_valid(str, token, strlen(token), addr); /* * Unfortunately, if our token has expired, we can no longer * validate the tokens of the remote peers, since they are using * a different set of keys. * * This means an expired GTKG will blindly trust well-formed remote * tokens at face value. But it's their fault, they should not run * an expired version! * --RAM, 2005-12-21 */ if (error == TOK_BAD_KEYS && tok_is_ancient(tm_time())) error = TOK_OK; /* Our keys have expired, cannot validate */ if (error != TOK_OK) { if (GNET_PROPERTY(version_debug)) { g_debug("vendor string \"%s\" [%s] has wrong token " "\"%s\": %s ", str, host_addr_to_string(addr), token, tok_strerror(error)); } return FALSE; } /* * OK, so now we know we can "trust" this version string as being * probably genuine. It makes sense to extract version information * out of it. */ } if (cmp > 0) /* We're more recent */ return TRUE; /* * If timestamp is greater and we were comparing against a stable * release, and cmp == 0, then this means an update in SVN about * a "released" version, probably alpha/beta. */ if ( cmp == 0 && (delta_time(their_version.timestamp, target_version->timestamp) > 0 || their_version.build > target_version->build) && target_version == &last_rel_version ) { if (GNET_PROPERTY(version_debug) > 3) g_debug("VERSION is a SVN update of a release"); if (version_build_cmp(&last_dev_version, &their_version) > 0) { if (GNET_PROPERTY(version_debug) > 3) g_debug("VERSION is less recent than latest dev we know"); return TRUE; } target_version = &last_dev_version; } /* * Their version is more recent, but is unstable -- only continue if * our version is also unstable. */ if (cmp < 0 && their_version.tag == 'u' && our_version.tag != 'u') return TRUE; if ( delta_time(their_version.timestamp, target_version->timestamp) < 0 || their_version.build <= target_version->build ) return TRUE; if ( delta_time(their_version.timestamp, our_version.timestamp) == 0 && their_version.build <= our_version.build ) return TRUE; /* * We found a more recent version than the last version seen. */ if (GNET_PROPERTY(version_debug) > 1) g_debug("more recent %s VERSION \"%s\"", target_version == &last_dev_version ? "dev" : "rel", str); *target_version = their_version; /* struct copy */ /* * Signal new version to user. * * Unless they run a development version, don't signal development * updates to them: they're probably not interested. */ version = version_ext_str(&their_version_ext, FALSE); /* No OS name */ g_message("more recent %s version of gtk-gnutella: %s", target_version == &last_dev_version ? "development" : "released", version); if (target_version == &last_rel_version) version_new_found(version, TRUE); else if (our_version.tag == 'u') version_new_found(version, FALSE); return TRUE; }
/** * Parse gtk-gnutella's version number in User-Agent/Server string `str' * and extract relevant information into `ver'. * * @param str the version string to be parsed * @param ver the structure filled with parsed information * @param end filled with a pointer to the first unparsed character * * @returns TRUE if we parsed a gtk-gnutella version correctly, FALSE if we * were not facing a gtk-gnutella version, or if we did not recognize it. */ static bool version_parse(const char *str, version_t *ver, const char **end) { const char *v; int error; /* * Modern version numbers are formatted like this: * * gtk-gnutella/0.85 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * gtk-gnutella/0.90u (24/06/2002; X11; Linux 2.4.18-pre7 i686) * gtk-gnutella/0.90b (24/06/2002; X11; Linux 2.4.18-2emi i686) * gtk-gnutella/0.90b2 (24/06/2002; X11; Linux 2.4.18-2emi i686) * * The letter after the version number is either 'u' for unstable, 'a' * for alpha, 'b' for beta, or nothing for a stable release. It can be * followed by digits when present. * * In prevision for future possible extensions, we also parse * * gtk-gnutella/0.90.1b2 (24/06/2002; X11; Linux 2.4.18-2emi i686) * * where the third number is the "patchlevel". * * Starting 2006-08-26, the user-agent string includes the SVN revision * at the time of the build. To be compatible with the servents out * there, the version is included as an optional tag following the date. * * gtk-gnutella/0.85 (04/04/2002; r3404; X11; FreeBSD 4.6-STABLE i386) * * However, we also start parsing the new format that we shall use when * all current GTKG out there have expired: * * gtk-gnutella/0.85-3404 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * gtk-gnutella/0.90b2-3404 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * * where the '-3404' introduces the build number. * * Since switching to git (2011-09-11), the SVN build is no longer present * but rather the output of "git describe" is used to show any difference * with the latest release tag. * * A release would be described as: * * gtk-gnutella/0.97.1 (2011-09-11; GTK1; Linux i686) * * but any change on top of that would be seen as: * * gtk-gnutella/0.97.1-24-g844b7 (2011-09-11; GTK1; Linux i686) * * to indicate that 24 commits were made after the release, and the commit * ID is 844b7... (only enough hexadecimal digits are output to make the * string unique at the time it was generated. * * If furthermore a build is done from a dirty git repository, the string * "-dirty" is appended after the commit ID: * * gtk-gnutella/0.97.1-24-g844b7-dirty (2011-09-11; GTK1; Linux i686) */ if (NULL == (v = is_strprefix(str, GTA_PRODUCT_NAME "/"))) return FALSE; /* * Parse "major.minor", followed by optional ".patchlevel". */ ver->major = parse_uint32(v, &v, 10, &error); if (error) return FALSE; if (*v++ != '.') return FALSE; ver->minor = parse_uint32(v, &v, 10, &error); if (error) return FALSE; if ('.' == *v) { v++; ver->patchlevel = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } else ver->patchlevel = 0; /* * Parse optional tag letter "x" followed by optional "taglevel". */ if (is_ascii_alpha(*v)) { ver->tag = *v++; if (is_ascii_digit(*v)) { ver->taglevel = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } else ver->taglevel = 0; } else { ver->tag = '\0'; ver->taglevel = 0; } /* * Parse optional "-build" (legacy SVN) or "-change-bxxxxxxxx" (git * version scheme) to be able to sort identical versions with distinct * changes applied to them. */ if ('-' == *v) { v++; if (!is_strprefix(v, "dirty")) { ver->build = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } } else ver->build = 0; if (end != NULL) *end = v; if (GNET_PROPERTY(version_debug) > 1) version_dump(str, ver, "#"); return TRUE; }