How to capture a variable query string, then append it to all URLs in a WordPress site

The $_GET global stores all query string entries, you should be able to use that. As for appending them to URLs, you could add a hook to the post_content filter to update any links in the post/page content, as well as hooks on the various permalink filters (e.g. page_link, post_link, etc.).

Personally, the filters approach will take a lot of work to make sure you cover all links on the site. I suggest instead you use javascript to update them all on page load. Here’s how you could do it in jQuery

$('a[href]').each(function(){
    var href = $(this).attr('href');

    if(href.indexOf('?') !== -1){
        // Query string exists, append current query string
        href += '&' + location.search.replace(/^\?/, '');
    }else{
        // No query string yet, add it
        href += location.search;
    }

    $(this).attr('href', href);
});

The location.search contains the query string of the page. The function will update ANY links it finds so you may want to refine it to only cover links that are relative to your site (test if this.href starts with your site’s home url.

If you have any links that may be created after page load, you’ll need to rework it into a click event that replaces the href attribute before it fires.

I’m not sure why you need to do this, but depending on the purpose it may be simpler to just have some custom code that stores data in their $_SESSION so that they’re remembered as having come to the site via the email, without having the query string preserved everywhere.

if(isset($_SESSION['source'])){
    $_SESSION['was_referred'] = true;
}
if(isset($_SESSION['was_referred'])){
    // do stuff
}