text:
Rebel Alliance Light Carriers?
Thread: Rebel Alliance Light Carriers?
2 July 2021, 07:24 AM #1
coldskier0320
Moderator: Roleplaying Forum
The Steel City
Hey all...I know this site is seldom traveled these days, but I'm hoping this might liven things up a bit for those who still occasionally visit!
Now then...
We know that for most of it's existence, the Rebel Alliance relied on its starfighters to project force, achieve objectives, and generally win the day. While they had ground troops, and even some capital ships, both of these were directly countered and overmatched by their equivalents in the Imperial forces. In short, if the rebels were using ground troops, it was to go against imperial ground troops...and in most (certainly not all, hence their existence) cases, anywhere that rebel troops could be deployed, imperial troops could be deployed faster, better supplied and supported, and in far greater numbers. Same goes for capships. So these forces would only take center stage in lopsided situations where their force could be brought to bear to achieve an objective, then everyone could get out of Dodge before an appropriate imperial response could be mounted.
While this was also broadly true for starfighters, in this one area, the rebels had the superior equipment...provided they could account for the discrepancies in numbers and piloting. As such, the Starfighter Corps took center stage in convoy and commerce raiding, hit-and-fade strikes, and the broader "space denial" tactics that, eventually, spread the Imp navy thin. Basically, Rebel fighters could appear at any time, nearly anywhere in the galaxy outside the core...and unless there were armed Imp vessels ready to serve in a defensive role, these rebel fighters could appear, hit, and be long gone before any aid at all could be scrambled, let alone arrive in time to do anything.
We all know that a huge advantage of the rebel fighters was that they were hyperspace capable. This had a dramatic impact on their ability to stage, attack, and retreat effectively. They were also capable of long range operations without capital ship support. Again, a huge advantage for an insurgency on a shoestring budget. However, while this independence was a huge advantage when actually carrying out a planned operation, at all other times, it would have been a logistical headache to move a squadron around without a mothership. With 12 fighters independent in space, there's no way for the pilots to practice personal hygiene or get a reasonable sleep. There's no support or repair available, nor any way to transport repair parts or technical crew to carry out those repairs anyway. There's no central location to plan, brief, or do anything else. You get the idea.
By the post-RotJ era, we see the Mon Cal cruisers form a stable capship backbone to the fleet, but prior to this, it seems to me that for the heavy focus the rebels placed on starfighter missions, we see precious few examples of cheap, durable craft capable of supporting 1-2 squadrons and their ops.
The lone example(s) I can find are the Quasar Fire class of carriers and, depending on who you ask, the Nebulon-B frigate, which holds anywhere from 0-2 squadrons, depending on who you ask (with RPG sources typically the most generous, at 2 squadrons plus shuttles...others saying 24 TIE but 12 of anything else, since TIEs are small, and "stackable" in racks...and still others arguing that there's simply no room for a hangar bay of any size in that spaceframe).
So with the Nebulon-B being up for debate, we've got the Quasar Fire, which is exactly the type of ship I'd expect to see the Alliance employing tons of. Holding anywhere from 4-6 squadrons, lightly armed and armored...basically, it's a mobile space garage and not much else. I'm good with that.
What surprises me though, is that we don't see anything else similar, but smaller and even more compact, customizable, and durable. A light carrier, capable of carrying 1-2 squadrons and supporting crew...and possibly a small contingent of troops...no more than 100, all told.
...or am I just missing something obvious?
W.W.G.D. - What would Grimace do?
What kind of dust?
Dirt-laced dust. Probably originating from the ground.
2 July 2021, 01:20 PM #2
Moderator: Rebellion Era
Great topic! You are right. There are only few known ship classes of the type of compact carriers. I am not yet familiar with all the new sources and series of the newer days, but I still have the old knowledge of the former and now abandoned legends. You often read in the novels that the Rebels used soem sort of modified bulk transport for their missions, but little is known on actual specifications. However I can remember the DarkStryder campain by West End Games. There they use a modified Corellian Corvette with a large hangar bay capable of holding one squadron of starfighters. As the Corellian Corvette often called a multi-purpose ship, it's at least very likely that several of those were withinin the Rebels rows as "auxiliary" carrier. Here is a link to images and data (not near my scanner currently):
https://starwars.fandom.com/wiki/FarStar
Definitely an interesting topic! Finally I can talk about and search for ships! I will get back if I find more.
Last edited by Deck; 2 July 2021 at 01:34 PM.
http://www.highadmiral.de
Thanks for the reminder about FarStar, although that's one that even pushes my limits of believability as far as its size vs capabilities. IIRC a CR90 is 150m long? Not saying a squadron could never fit in there, but it seems like it's playing very fast and loose with even the Star Wars universe's disregard for inside vs outside dimensions.
Still, that leaves only the Quasar Fire class, and a one-of-a-kind heavily modified corvette filling this role. In light of the info we have about the inter-trilogy period, I would think that this specific role would be the single most common capital ship in the rebel fleet. Maybe they wouldn't all look the same, but I'd imagine that the process of converting a bulk freighter into a light carrier would be one that many rebel wrench monkeys would be familiar with, to the point that there would be a healthy exchange of ideas between crews when two of them were assigned together for an op.
Hello again! Well, there's still the Neutron Star Bulk Cruiser as mentioned in the first and original Thrawn Trilogy. It also appears in the WEG sourcebooks for the Thrawn Triogy and the Rebel Alliance Sourcebook. It's a refitted bulk cruiser to serve es a carrier, 600 meters in length, and capable of holding 3 squadrons of starfighters.
It's also interesting to read the Truce at Bakura Sourcebook, the original source for the Quasar-fire-class cruiser carrier. If you stick with these "old" sources, there's indeed no other carrier ship besides the MC80 cruisers, the NeutronStar and the QuasarFire.
D6 Forever!
the great western state of Montana!
Have you looked into any of the "lost ships of Star Wars" on YouTube? They're done by EC Henry and are pretty good overall. One video in particular, this one: https://youtu.be/dtozC1NLZY8 , talks about a Mon Calamari ship that was capable of holding up to a full squadron of fighters. I'm pretty sure you could easily use other Mon Cal ships in the "light carrier" capability.
Try not. Do or do not, there is no try.
facta, non verba
13 July 2021, 07:24 AM #6
Thanks gentlemen!
Yes, Deck, I'm familiar with the Bulk Cruiser, although I didn't know the specific name of the model in question. I'm sure the Rebels did plenty of heavy mods of old cargo haulers...I guess I'm just surprised not to see a more widespread utilization of ships in that role.
Grimace, thanks for that link! You've sent me down a rabbit hole of his videos, and I'm enjoying most of them!
16 July 2021, 05:43 PM #7
Hey, you're welcome. I've been enticed by a few of his videos, specifically the "forgotten ships" videos.
Quick Navigation Rebellion Era Top


text:
Tom Hullena  is a former principal at Makoura College he worked at the College from 2009 to 2015. He also taught at Kuranui College as a HOD physical Education. Tom Hullena received the MNZM for services to education in 2016.

Career
He worked mostly 70 hours a week or more over seven years while principal at Makoura. He has been an educator for more than three decades and now helps lead the development of Youth in Education, Training and Employment schemes for the Ministry of Education. He spent three years with Victoria University as a lecturer, researcher and teacher training adviser from 2005 to 2008. In 2014 he received the Prime Minister's Excellence Award for Leadership.

References

Members of the New Zealand Order of Merit
People educated at Wairarapa College
Living people
Year of birth missing (living people)

redpajama_set_name: RedPajamaWikipedia


text:
Q: Package nicematrix - "TeX capacity exceeded" error I am trying to reproduce the example from Section 16.3 of the nicematrix manual:
\documentclass[10pt,a4paper]{article}

\usepackage{nicematrix}

\begin{document}
\NiceMatrixOptions{nullify-dots,code-for-first-col = \color{blue},code-for-first-col=\color{blue}}
$\begin{pNiceMatrix}[first-row,first-col]
&   & \Ldots[line-style={solid,<->},shorten=0pt]^{n \text{ columns}} \\
& 1 & 1 & 1 & \Ldots & 1 \\
& 1 & 1 & 1 &  & 1 \\
\Vdots[line-style={solid,<->}]_{n \text{ rows}} & 1 & 1 & 1 &  & 1 \\
& 1 & 1 & 1 &  & 1 \\& 1 & 1 & 1 & \Ldots & 1
\end{pNiceMatrix}$
\end{document}

However, I keep getting the "TeX capacity exceeded" error.
I boiled it down to the following MWE.
\documentclass[10pt,a4paper]{article}

\usepackage{nicematrix}

\begin{document}
    $\begin{pNiceMatrix}[first-row,first-col]
    &   & \Ldots & \\
    & 1 & 1 & 1 & \Ldots & 1 \\
    & 1 & 1 & 1 &  & 1 \\
    \Vdots & 1 & 1 & 1 &  & 1 \\
    & 1 & 1 & 1 &  & 1 \\
    & 1 & 1 & 1 & \Ldots & 1
    \end{pNiceMatrix}$
\end{document}

It still produces the same error.
P.S. I am compiling via TeXstudio on Ubuntu 20.04.1 LTS.
P.P.S. The full log file can be found here: https://pastebin.com/W6dVWMc9

A: The solution to that problem is to update the package nicematrix.

redpajama_set_name: RedPajamaStackExchange


text:
Promise of the Day: 'TeamBJ' Vows to Give Every Bad Hombre Who Votes Against Trump a Special Reward
Via OfficialTeamBJ
These two 'nasty women' have made a promise that's going to be pretty hard to keep following election day.
For one thing, commenters have pointed out that an actual exchange of votes for BJs is very illegal (not to mention difficult to verify).
Another unintended consequence of this video is the slew of terrible comments. With the exception of Jeff here...
...most of the commenters were unappreciative of the offer.
lncest of the Day: Adult Film Star Madeline Madison is Suing Over Unwittingly Giving Her Brother a BJ
Via Elite Daily
We're not sure how many OSHA laws this violates.
When Madeline Madison showed up to shoot Milk Maids 2 (the first one must have been a real success to spin off a sequel), she was confused why she wouldn't meet the man on whom she would perform fellatio. You know, for the fans out there.
In an interview covered inElite Daily Madison said:
The producers wouldn't let me meet the man I was sucking off beforehand, which I found odd, because normally we always meet and greet our scene partner. I like to make an emotional connection with my fellow actor before a scene. I even try to quickly fall in love with them, if I can, as it makes my job so much easier for me if I'm truly in love with the man I'm about ready to f*ck.
Seems like a tricky situation and one, in any other workplace, that would be a deal breaker.
But in the fast-paced, high-powered world of adult film, you commit to your sexual encounters.
Unfortunately, and allegedly unbeknownst to Madison, the fellatio scene was a glory hole romp that featured her little brother Chuck Tang on the other side of the wall.
When Madison found out, she said she was devastated and is suing the film company for $3.2 million.
The director, Jameel Mendoza, took this whole thing down to an even grosser level, by insisting Madison relates and incorrect version of the events.
She's a liar. [She] will do anything for money. We normally would have paid a third-rate talent like her $25 for a single glory hole scene, but we agreed to $100 because it was with her brother. Let's not forget this is the same girl who allowed herself to be penetrated by the head of a dead Anaconda in Snake Hole. She has no moral standards.
If you're looking for someone to blame for this buffet of sin, Madison hints that you should point your finger at Japan.
The company doesn't show real inc*st with past titles, but with me and my little brother being known, they've now caught real inc*st on camera which will make them billions in Japan. The Japanese love inc*st.
Who would have thought the adult film industry was so full of unsavory characters?
madeline madison

redpajama_set_name: RedPajamaCommonCrawl


This year, like every year in the ZLA, has been dedicated to the details. Throughout 2016, we've reviewed hundreds of products, for thousands of miles, in a relentless quest to find the best gear for those who make their way on two-wheels. These are the ZLA Awards.
Like Neil Armstrong circa 1969, HJC took one giant leap for all helmet-kind in 2016. With the RPHA 11 Pro, HJC has produced a premium race helmet that comes with Dark Smoke and Clear face shields, is 7 ounces lighter, and provides some of the best ventilation we've ever seen. Oh yeah, and it also offers one of the meanest Marvel graphics you will ever see!
As a robot cowboy atop a steel-framed stallion, the REV'IT! Akira Air Vintage rides the line between aesthetics of the old and technology of the new. As the ZLA selection for leather jacket of the year, the Akira Air Vintage has the look of a wild-west outlaw - if that outlaw were rocking a MotoGP-inspired, impeccably armored, and precision-tailored motorcycle jacket.
Substance and style: Dainese does it like no other. The Super Rider D-Dry Jacket is a shining example of precisely that, which is why we named it the best textile jacket of the year. Combining premium Tutu leather, 3D mesh, Mugello stretch fabric, and a removable D-Dry thermal membrane, the Super Rider D-Dry provides the pinnacle of 3 season sport jacket wearability.
In an ever-expanding field of quality motorcycle footwear, the best motorcycle boots of the year are actually a pair of motorcycle shoes. Rugged and resilient, yet intricately refined, the Stylmartin Iron Riding Shoes set themselves apart with a combination of CE-certified protection, waterproofing, and high-fashion details such as their hand-waxed Graffiti leather construction.
Getting to the top is hard, and some are content to rest when they arrive. Not REV'IT!. They work harder. With advanced protection, improved ergonomics, and a bonded H2O waterproof membrane that improves tactile responsiveness, the REV'IT! Summit 2 Gloves have substantially improved over the original and give REV'IT! a second straight win for best gloves of the year.
For adding a much-needed addition to the limited selections currently available for women motocross riders, the SIDI X-3 Lei Women's Boots are our pick for the best women's motorcycle product of the year. As the most aggressive, high-end option on the market, the X-3 Lei Boots deliver pro-level features in a purpose-built, off-road boot specifically designed to better fit women riders.
In making motorcycling safer for the masses, the Helite Turtle Airbag Vest stands as a landmark technological innovation for 2016. Built on the principles of protection that have previously only been available in moto-racing's elite circles, Helite has crafted a lightweight airbag vest for everyday riders that offers advanced defenses for vital organs and the spine in the event of a crash.
Like a Kevlar-lined ninja on a moonless midnight ride, the Klim K Fifty 1 Jeans made their way into the mix and took the industry by surprise in 2016. With D3O armor at the knees and hips, as well as a Cordura / Denim combo outer shell that is backed by Kevlar reinforcements, the Klim K Fifty 1 Jeans are a casual-fitting, low-key street-style option from the kings of the off-road.
"Grip it and rip it" is a two way trail. Even the most aggressive throttle pin goes nowhere without tires that translate the power. Built to the exacting demands of some of the best MX riders on the planet, the Bridgestone BattleCross X30 Tires became our favorite of the year based on their balance of linear traction, stability, and responsiveness that allowed riders to perform confidently at full-tilt.
Iron Man had J.A.R.V.I.S, Luke had R2-D2, and your motorcycle has the Motogadget m-Unit V.2 Digital Control and Fuse Box. Yeah, it's a lot to say, but with the time and frustration it saves while wiring / re-wiring your bike, you can definitely come up with a good nickname to describe the integral role it plays as the brains of your motorcycle's electrical system.
Developed for dirt riders who ask as much out of their bikes as they do out of themselves in the pursuit of a mud-caked victory, the Rekluse Core EXP 3.0 Clutch Kit is the pinnacle of centrifugal clutch performance. By automatically engaging / disengaging the clutch based on RPM, the Core EXP 3.0 Kit from Rekluse allows riders to be more precise through every facet of riding.
Mirror, mirror on the bar, thanks for the view of that car from afar! -- After a year of reviewing bikes that came with OEM mirrors that left a lot to be desired in both view range and aesthetics, the combination of minimalist styling and maximum vision that comes with the CRG Lane Splitter Bar End Mirrors is the reason they are our safety part of the year.
Time: It is the most valuable thing on Earth. Money: Yeah, that's pretty valuable too! Our best motorcycle accessory of the year helps you save both. Trackside Paddock Stands work to make maintenance on your bike easier, faster, and generally more efficient so that you can enjoy more time on the tarmac and more laps in a lifetime.
Every year, the ZLA Awards honor the best innovations in motorcycle gear. But we also pass the megaphone to some of our expert Zillans to expound on why certain products won or to highlight other products that still deserve recognition.
Spurg, Lemmy and Rob (our in-house Dirt riding expert) want to tell you about an award-winner and another personal favorite that just missed making the list.
RevZilla gives out ZLA Awards for moto gear and for parts and accessories because we evaluate all that stuff before we offer it for sale to you. We don't sell motorcycles, but if you weren't interested in motorcycles, you wouldn't need the gear or parts, right?.


Please accept our sincere condolences as you plan a Memorial Funeral Luncheon to honor your loved one. A Funeral or Memorial Luncheon provides an opportunity for family members and close friends to share moments of companionship during this difficult time. We can help make this a pleasant, memorable, and trouble free experience for your family. We'll relieve you of all worry over details while assuring your comfort and the comfort of your relatives and guests.
Special requests can be accommodated, whether it be an ethnic food detail or a photo display in your private room. Our experienced staff will make all the arrangements for your luncheon. For your convenience, all details can be finalized over the phone.


text:
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

this.EXPORTED_SYMBOLS = ["Service"];

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

// How long before refreshing the cluster
const CLUSTER_BACKOFF = 5 * 60 * 1000; // 5 minutes

// How long a key to generate from an old passphrase.
const PBKDF2_KEY_BYTES = 16;

const CRYPTO_COLLECTION = "crypto";
const KEYS_WBO = "keys";

Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/rest.js");
Cu.import("resource://services-sync/stages/enginesync.js");
Cu.import("resource://services-sync/stages/declined.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/userapi.js");
Cu.import("resource://services-sync/util.js");

const ENGINE_MODULES = {
  Addons: "addons.js",
  Bookmarks: "bookmarks.js",
  Form: "forms.js",
  History: "history.js",
  Password: "passwords.js",
  Prefs: "prefs.js",
  Tab: "tabs.js",
};

const STORAGE_INFO_TYPES = [INFO_COLLECTIONS,
                            INFO_COLLECTION_USAGE,
                            INFO_COLLECTION_COUNTS,
                            INFO_QUOTA];

// A structure mapping a (boolean) telemetry probe name to a preference name.
// The probe will record true if the pref is modified, false otherwise.
const TELEMETRY_CUSTOM_SERVER_PREFS = {
  WEAVE_CUSTOM_LEGACY_SERVER_CONFIGURATION: "services.sync.serverURL",
  WEAVE_CUSTOM_FXA_SERVER_CONFIGURATION: "identity.fxaccounts.auth.uri",
  WEAVE_CUSTOM_TOKEN_SERVER_CONFIGURATION: "services.sync.tokenServerURI",
};

function Sync11Service() {
  this._notify = Utils.notify("weave:service:");
}
Sync11Service.prototype = {

  _lock: Utils.lock,
  _locked: false,
  _loggedIn: false,

  infoURL: null,
  storageURL: null,
  metaURL: null,
  cryptoKeyURL: null,

  get serverURL() Svc.Prefs.get("serverURL"),
  set serverURL(value) {
    if (!value.endsWith("/")) {
      value += "/";
    }

    // Only do work if it's actually changing
    if (value == this.serverURL)
      return;

    // A new server most likely uses a different cluster, so clear that
    Svc.Prefs.set("serverURL", value);
    Svc.Prefs.reset("clusterURL");
  },

  get clusterURL() Svc.Prefs.get("clusterURL", ""),
  set clusterURL(value) {
    Svc.Prefs.set("clusterURL", value);
    this._updateCachedURLs();
  },

  get miscAPI() {
    // Append to the serverURL if it's a relative fragment
    let misc = Svc.Prefs.get("miscURL");
    if (misc.indexOf(":") == -1)
      misc = this.serverURL + misc;
    return misc + MISC_API_VERSION + "/";
  },

  /**
   * The URI of the User API service.
   *
   * This is the base URI of the service as applicable to all users up to
   * and including the server version path component, complete with trailing
   * forward slash.
   */
  get userAPIURI() {
    // Append to the serverURL if it's a relative fragment.
    let url = Svc.Prefs.get("userURL");
    if (!url.contains(":")) {
      url = this.serverURL + url;
    }

    return url + USER_API_VERSION + "/";
  },

  get pwResetURL() {
    return this.serverURL + "weave-password-reset";
  },

  get syncID() {
    // Generate a random syncID id we don't have one
    let syncID = Svc.Prefs.get("client.syncID", "");
    return syncID == "" ? this.syncID = Utils.makeGUID() : syncID;
  },
  set syncID(value) {
    Svc.Prefs.set("client.syncID", value);
  },

  get isLoggedIn() { return this._loggedIn; },

  get locked() { return this._locked; },
  lock: function lock() {
    if (this._locked)
      return false;
    this._locked = true;
    return true;
  },
  unlock: function unlock() {
    this._locked = false;
  },

  // A specialized variant of Utils.catch.
  // This provides a more informative error message when we're already syncing:
  // see Bug 616568.
  _catch: function _catch(func) {
    function lockExceptions(ex) {
      if (Utils.isLockException(ex)) {
        // This only happens if we're syncing already.
        this._log.info("Cannot start sync: already syncing?");
      }
    }

    return Utils.catch.call(this, func, lockExceptions);
  },

  get userBaseURL() {
    if (!this._clusterManager) {
      return null;
    }
    return this._clusterManager.getUserBaseURL();
  },

  _updateCachedURLs: function _updateCachedURLs() {
    // Nothing to cache yet if we don't have the building blocks
    if (!this.clusterURL || !this.identity.username)
      return;

    this._log.debug("Caching URLs under storage user base: " + this.userBaseURL);

    // Generate and cache various URLs under the storage API for this user
    this.infoURL = this.userBaseURL + "info/collections";
    this.storageURL = this.userBaseURL + "storage/";
    this.metaURL = this.storageURL + "meta/global";
    this.cryptoKeysURL = this.storageURL + CRYPTO_COLLECTION + "/" + KEYS_WBO;
  },

  _checkCrypto: function _checkCrypto() {
    let ok = false;

    try {
      let iv = Svc.Crypto.generateRandomIV();
      if (iv.length == 24)
        ok = true;

    } catch (e) {
      this._log.debug("Crypto check failed: " + e);
    }

    return ok;
  },

  /**
   * Here is a disgusting yet reasonable way of handling HMAC errors deep in
   * the guts of Sync. The astute reader will note that this is a hacky way of
   * implementing something like continuable conditions.
   *
   * A handler function is glued to each engine. If the engine discovers an
   * HMAC failure, we fetch keys from the server and update our keys, just as
   * we would on startup.
   *
   * If our key collection changed, we signal to the engine (via our return
   * value) that it should retry decryption.
   *
   * If our key collection did not change, it means that we already had the
   * correct keys... and thus a different client has the wrong ones. Reupload
   * the bundle that we fetched, which will bump the modified time on the
   * server and (we hope) prompt a broken client to fix itself.
   *
   * We keep track of the time at which we last applied this reasoning, because
   * thrashing doesn't solve anything. We keep a reasonable interval between
   * these remedial actions.
   */
  lastHMACEvent: 0,

  /*
   * Returns whether to try again.
   */
  handleHMACEvent: function handleHMACEvent() {
    let now = Date.now();

    // Leave a sizable delay between HMAC recovery attempts. This gives us
    // time for another client to fix themselves if we touch the record.
    if ((now - this.lastHMACEvent) < HMAC_EVENT_INTERVAL)
      return false;

    this._log.info("Bad HMAC event detected. Attempting recovery " +
                   "or signaling to other clients.");

    // Set the last handled time so that we don't act again.
    this.lastHMACEvent = now;

    // Fetch keys.
    let cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
    try {
      let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;

      // Save out the ciphertext for when we reupload. If there's a bug in
      // CollectionKeyManager, this will prevent us from uploading junk.
      let cipherText = cryptoKeys.ciphertext;

      if (!cryptoResp.success) {
        this._log.warn("Failed to download keys.");
        return false;
      }

      let keysChanged = this.handleFetchedKeys(this.identity.syncKeyBundle,
                                               cryptoKeys, true);
      if (keysChanged) {
        // Did they change? If so, carry on.
        this._log.info("Suggesting retry.");
        return true;              // Try again.
      }

      // If not, reupload them and continue the current sync.
      cryptoKeys.ciphertext = cipherText;
      cryptoKeys.cleartext  = null;

      let uploadResp = cryptoKeys.upload(this.resource(this.cryptoKeysURL));
      if (uploadResp.success)
        this._log.info("Successfully re-uploaded keys. Continuing sync.");
      else
        this._log.warn("Got error response re-uploading keys. " +
                       "Continuing sync; let's try again later.");

      return false;            // Don't try again: same keys.

    } catch (ex) {
      this._log.warn("Got exception \"" + ex + "\" fetching and handling " +
                     "crypto keys. Will try again later.");
      return false;
    }
  },

  handleFetchedKeys: function handleFetchedKeys(syncKey, cryptoKeys, skipReset) {
    // Don't want to wipe if we're just starting up!
    let wasBlank = this.collectionKeys.isClear;
    let keysChanged = this.collectionKeys.updateContents(syncKey, cryptoKeys);

    if (keysChanged && !wasBlank) {
      this._log.debug("Keys changed: " + JSON.stringify(keysChanged));

      if (!skipReset) {
        this._log.info("Resetting client to reflect key change.");

        if (keysChanged.length) {
          // Collection keys only. Reset individual engines.
          this.resetClient(keysChanged);
        }
        else {
          // Default key changed: wipe it all.
          this.resetClient();
        }

        this._log.info("Downloaded new keys, client reset. Proceeding.");
      }
      return true;
    }
    return false;
  },

  /**
   * Prepare to initialize the rest of Weave after waiting a little bit
   */
  onStartup: function onStartup() {
    this._migratePrefs();

    // Status is instantiated before us and is the first to grab an instance of
    // the IdentityManager. We use that instance because IdentityManager really
    // needs to be a singleton. Ideally, the longer-lived object would spawn
    // this service instance.
    if (!Status || !Status._authManager) {
      throw new Error("Status or Status._authManager not initialized.");
    }

    this.status = Status;
    this.identity = Status._authManager;
    this.collectionKeys = new CollectionKeyManager();

    this.errorHandler = new ErrorHandler(this);

    this._log = Log.repository.getLogger("Sync.Service");
    this._log.level =
      Log.Level[Svc.Prefs.get("log.logger.service.main")];

    this._log.info("Loading Weave " + WEAVE_VERSION);

    this._clusterManager = this.identity.createClusterManager(this);
    this.recordManager = new RecordManager(this);

    this.enabled = true;

    this._registerEngines();

    let ua = Cc["@mozilla.org/network/protocol;1?name=http"].
      getService(Ci.nsIHttpProtocolHandler).userAgent;
    this._log.info(ua);

    if (!this._checkCrypto()) {
      this.enabled = false;
      this._log.info("Could not load the Weave crypto component. Disabling " +
                      "Weave, since it will not work correctly.");
    }

    Svc.Obs.add("weave:service:setup-complete", this);
    Svc.Prefs.observe("engine.", this);

    this.scheduler = new SyncScheduler(this);

    if (!this.enabled) {
      this._log.info("Firefox Sync disabled.");
    }

    this._updateCachedURLs();

    let status = this._checkSetup();
    if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED) {
      Svc.Obs.notify("weave:engine:start-tracking");
    }

    // Telemetry probes to indicate if the user is using custom servers.
    for (let [probeName, prefName] of Iterator(TELEMETRY_CUSTOM_SERVER_PREFS)) {
      let isCustomized = Services.prefs.prefHasUserValue(prefName);
      Services.telemetry.getHistogramById(probeName).add(isCustomized);
    }

    // Send an event now that Weave service is ready.  We don't do this
    // synchronously so that observers can import this module before
    // registering an observer.
    Utils.nextTick(function onNextTick() {
      this.status.ready = true;

      // UI code uses the flag on the XPCOM service so it doesn't have
      // to load a bunch of modules.
      let xps = Cc["@mozilla.org/weave/service;1"]
                  .getService(Ci.nsISupports)
                  .wrappedJSObject;
      xps.ready = true;

      Svc.Obs.notify("weave:service:ready");
    }.bind(this));
  },

  _checkSetup: function _checkSetup() {
    if (!this.enabled) {
      return this.status.service = STATUS_DISABLED;
    }
    return this.status.checkSetup();
  },

  _migratePrefs: function _migratePrefs() {
    // Migrate old debugLog prefs.
    let logLevel = Svc.Prefs.get("log.appender.debugLog");
    if (logLevel) {
      Svc.Prefs.set("log.appender.file.level", logLevel);
      Svc.Prefs.reset("log.appender.debugLog");
    }
    if (Svc.Prefs.get("log.appender.debugLog.enabled")) {
      Svc.Prefs.set("log.appender.file.logOnSuccess", true);
      Svc.Prefs.reset("log.appender.debugLog.enabled");
    }

    // Migrate old extensions.weave.* prefs if we haven't already tried.
    if (Svc.Prefs.get("migrated", false))
      return;

    // Grab the list of old pref names
    let oldPrefBranch = "extensions.weave.";
    let oldPrefNames = Cc["@mozilla.org/preferences-service;1"].
                       getService(Ci.nsIPrefService).
                       getBranch(oldPrefBranch).
                       getChildList("", {});

    // Map each old pref to the current pref branch
    let oldPref = new Preferences(oldPrefBranch);
    for each (let pref in oldPrefNames)
      Svc.Prefs.set(pref, oldPref.get(pref));

    // Remove all the old prefs and remember that we've migrated
    oldPref.resetBranch("");
    Svc.Prefs.set("migrated", true);
  },

  /**
   * Register the built-in engines for certain applications
   */
  _registerEngines: function _registerEngines() {
    this.engineManager = new EngineManager(this);

    let engines = [];
    // Applications can provide this preference (comma-separated list)
    // to specify which engines should be registered on startup.
    let pref = Svc.Prefs.get("registerEngines");
    if (pref) {
      engines = pref.split(",");
    }

    let declined = [];
    pref = Svc.Prefs.get("declinedEngines");
    if (pref) {
      declined = pref.split(",");
    }

    this.clientsEngine = new ClientEngine(this);

    for (let name of engines) {
      if (!name in ENGINE_MODULES) {
        this._log.info("Do not know about engine: " + name);
        continue;
      }

      let ns = {};
      try {
        Cu.import("resource://services-sync/engines/" + ENGINE_MODULES[name], ns);

        let engineName = name + "Engine";
        if (!(engineName in ns)) {
          this._log.warn("Could not find exported engine instance: " + engineName);
          continue;
        }

        this.engineManager.register(ns[engineName]);
      } catch (ex) {
        this._log.warn("Could not register engine " + name + ": " +
                       CommonUtils.exceptionStr(ex));
      }
    }

    this.engineManager.setDeclined(declined);
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),

  // nsIObserver

  observe: function observe(subject, topic, data) {
    switch (topic) {
      case "weave:service:setup-complete":
        let status = this._checkSetup();
        if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED)
            Svc.Obs.notify("weave:engine:start-tracking");
        break;
      case "nsPref:changed":
        if (this._ignorePrefObserver)
          return;
        let engine = data.slice((PREFS_BRANCH + "engine.").length);
        this._handleEngineStatusChanged(engine);
        break;
    }
  },

  _handleEngineStatusChanged: function handleEngineDisabled(engine) {
    this._log.trace("Status for " + engine + " engine changed.");
    if (Svc.Prefs.get("engineStatusChanged." + engine, false)) {
      // The enabled status being changed back to what it was before.
      Svc.Prefs.reset("engineStatusChanged." + engine);
    } else {
      // Remember that the engine status changed locally until the next sync.
      Svc.Prefs.set("engineStatusChanged." + engine, true);
    }
  },

  /**
   * Obtain a Resource instance with authentication credentials.
   */
  resource: function resource(url) {
    let res = new Resource(url);
    res.authenticator = this.identity.getResourceAuthenticator();

    return res;
  },

  /**
   * Obtain a SyncStorageRequest instance with authentication credentials.
   */
  getStorageRequest: function getStorageRequest(url) {
    let request = new SyncStorageRequest(url);
    request.authenticator = this.identity.getRESTRequestAuthenticator();

    return request;
  },

  /**
   * Perform the info fetch as part of a login or key fetch, or
   * inside engine sync.
   */
  _fetchInfo: function (url) {
    let infoURL = url || this.infoURL;

    this._log.trace("In _fetchInfo: " + infoURL);
    let info;
    try {
      info = this.resource(infoURL).get();
    } catch (ex) {
      this.errorHandler.checkServerError(ex);
      throw ex;
    }

    // Always check for errors; this is also where we look for X-Weave-Alert.
    this.errorHandler.checkServerError(info);
    if (!info.success) {
      throw "Aborting sync: failed to get collections.";
    }
    return info;
  },

  verifyAndFetchSymmetricKeys: function verifyAndFetchSymmetricKeys(infoResponse) {

    this._log.debug("Fetching and verifying -- or generating -- symmetric keys.");

    // Don't allow empty/missing passphrase.
    // Furthermore, we assume that our sync key is already upgraded,
    // and fail if that assumption is invalidated.

    if (!this.identity.syncKey) {
      this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
      this.status.sync = CREDENTIALS_CHANGED;
      return false;
    }

    let syncKeyBundle = this.identity.syncKeyBundle;
    if (!syncKeyBundle) {
      this._log.error("Sync Key Bundle not set. Invalid Sync Key?");

      this.status.login = LOGIN_FAILED_INVALID_PASSPHRASE;
      this.status.sync = CREDENTIALS_CHANGED;
      return false;
    }

    try {
      if (!infoResponse)
        infoResponse = this._fetchInfo();    // Will throw an exception on failure.

      // This only applies when the server is already at version 4.
      if (infoResponse.status != 200) {
        this._log.warn("info/collections returned non-200 response. Failing key fetch.");
        this.status.login = LOGIN_FAILED_SERVER_ERROR;
        this.errorHandler.checkServerError(infoResponse);
        return false;
      }

      let infoCollections = infoResponse.obj;

      this._log.info("Testing info/collections: " + JSON.stringify(infoCollections));

      if (this.collectionKeys.updateNeeded(infoCollections)) {
        this._log.info("collection keys reports that a key update is needed.");

        // Don't always set to CREDENTIALS_CHANGED -- we will probably take care of this.

        // Fetch storage/crypto/keys.
        let cryptoKeys;

        if (infoCollections && (CRYPTO_COLLECTION in infoCollections)) {
          try {
            cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
            let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;

            if (cryptoResp.success) {
              let keysChanged = this.handleFetchedKeys(syncKeyBundle, cryptoKeys);
              return true;
            }
            else if (cryptoResp.status == 404) {
              // On failure, ask to generate new keys and upload them.
              // Fall through to the behavior below.
              this._log.warn("Got 404 for crypto/keys, but 'crypto' in info/collections. Regenerating.");
              cryptoKeys = null;
            }
            else {
              // Some other problem.
              this.status.login = LOGIN_FAILED_SERVER_ERROR;
              this.errorHandler.checkServerError(cryptoResp);
              this._log.warn("Got status " + cryptoResp.status + " fetching crypto keys.");
              return false;
            }
          }
          catch (ex) {
            this._log.warn("Got exception \"" + ex + "\" fetching cryptoKeys.");
            // TODO: Um, what exceptions might we get here? Should we re-throw any?

            // One kind of exception: HMAC failure.
            if (Utils.isHMACMismatch(ex)) {
              this.status.login = LOGIN_FAILED_INVALID_PASSPHRASE;
              this.status.sync = CREDENTIALS_CHANGED;
            }
            else {
              // In the absence of further disambiguation or more precise
              // failure constants, just report failure.
              this.status.login = LOGIN_FAILED;
            }
            return false;
          }
        }
        else {
          this._log.info("... 'crypto' is not a reported collection. Generating new keys.");
        }

        if (!cryptoKeys) {
          this._log.info("No keys! Generating new ones.");

          // Better make some and upload them, and wipe the server to ensure
          // consistency. This is all achieved via _freshStart.
          // If _freshStart fails to clear the server or upload keys, it will
          // throw.
          this._freshStart();
          return true;
        }

        // Last-ditch case.
        return false;
      }
      else {
        // No update needed: we're good!
        return true;
      }

    } catch (ex) {
      // This means no keys are present, or there's a network error.
      this._log.debug("Failed to fetch and verify keys: "
                      + Utils.exceptionStr(ex));
      this.errorHandler.checkServerError(ex);
      return false;
    }
  },

  verifyLogin: function verifyLogin(allow40XRecovery = true) {
    // If the identity isn't ready it  might not know the username...
    if (!this.identity.readyToAuthenticate) {
      this._log.info("Not ready to authenticate in verifyLogin.");
      this.status.login = LOGIN_FAILED_NOT_READY;
      return false;
    }

    if (!this.identity.username) {
      this._log.warn("No username in verifyLogin.");
      this.status.login = LOGIN_FAILED_NO_USERNAME;
      return false;
    }

    // Unlock master password, or return.
    // Attaching auth credentials to a request requires access to
    // passwords, which means that Resource.get can throw MP-related
    // exceptions!
    // Try to fetch the passphrase first, while we still have control.
    try {
      this.identity.syncKey;
    } catch (ex) {
      this._log.debug("Fetching passphrase threw " + ex +
                      "; assuming master password locked.");
      this.status.login = MASTER_PASSWORD_LOCKED;
      return false;
    }

    try {
      // Make sure we have a cluster to verify against.
      // This is a little weird, if we don't get a node we pretend
      // to succeed, since that probably means we just don't have storage.
      if (this.clusterURL == "" && !this._clusterManager.setCluster()) {
        this.status.sync = NO_SYNC_NODE_FOUND;
        return true;
      }

      // Fetch collection info on every startup.
      let test = this.resource(this.infoURL).get();

      switch (test.status) {
        case 200:
          // The user is authenticated.

          // We have no way of verifying the passphrase right now,
          // so wait until remoteSetup to do so.
          // Just make the most trivial checks.
          if (!this.identity.syncKey) {
            this._log.warn("No passphrase in verifyLogin.");
            this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
            return false;
          }

          // Go ahead and do remote setup, so that we can determine
          // conclusively that our passphrase is correct.
          if (this._remoteSetup(test)) {
            // Username/password verified.
            this.status.login = LOGIN_SUCCEEDED;
            return true;
          }

          this._log.warn("Remote setup failed.");
          // Remote setup must have failed.
          return false;

        case 401:
          this._log.warn("401: login failed.");
          // Fall through to the 404 case.

        case 404:
          // Check that we're verifying with the correct cluster
          if (allow40XRecovery && this._clusterManager.setCluster()) {
            return this.verifyLogin(false);
          }

          // We must have the right cluster, but the server doesn't expect us
          this.status.login = LOGIN_FAILED_LOGIN_REJECTED;
          return false;

        default:
          // Server didn't respond with something that we expected
          this.status.login = LOGIN_FAILED_SERVER_ERROR;
          this.errorHandler.checkServerError(test);
          return false;
      }
    } catch (ex) {
      // Must have failed on some network issue
      this._log.debug("verifyLogin failed: " + Utils.exceptionStr(ex));
      this.status.login = LOGIN_FAILED_NETWORK_ERROR;
      this.errorHandler.checkServerError(ex);
      return false;
    }
  },

  generateNewSymmetricKeys: function generateNewSymmetricKeys() {
    this._log.info("Generating new keys WBO...");
    let wbo = this.collectionKeys.generateNewKeysWBO();
    this._log.info("Encrypting new key bundle.");
    wbo.encrypt(this.identity.syncKeyBundle);

    this._log.info("Uploading...");
    let uploadRes = wbo.upload(this.resource(this.cryptoKeysURL));
    if (uploadRes.status != 200) {
      this._log.warn("Got status " + uploadRes.status + " uploading new keys. What to do? Throw!");
      this.errorHandler.checkServerError(uploadRes);
      throw new Error("Unable to upload symmetric keys.");
    }
    this._log.info("Got status " + uploadRes.status + " uploading keys.");
    let serverModified = uploadRes.obj;   // Modified timestamp according to server.
    this._log.debug("Server reports crypto modified: " + serverModified);

    // Now verify that info/collections shows them!
    this._log.debug("Verifying server collection records.");
    let info = this._fetchInfo();
    this._log.debug("info/collections is: " + info);

    if (info.status != 200) {
      this._log.warn("Non-200 info/collections response. Aborting.");
      throw new Error("Unable to upload symmetric keys.");
    }

    info = info.obj;
    if (!(CRYPTO_COLLECTION in info)) {
      this._log.error("Consistency failure: info/collections excludes " +
                      "crypto after successful upload.");
      throw new Error("Symmetric key upload failed.");
    }

    // Can't check against local modified: clock drift.
    if (info[CRYPTO_COLLECTION] < serverModified) {
      this._log.error("Consistency failure: info/collections crypto entry " +
                      "is stale after successful upload.");
      throw new Error("Symmetric key upload failed.");
    }

    // Doesn't matter if the timestamp is ahead.

    // Download and install them.
    let cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
    let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;
    if (cryptoResp.status != 200) {
      this._log.warn("Failed to download keys.");
      throw new Error("Symmetric key download failed.");
    }
    let keysChanged = this.handleFetchedKeys(this.identity.syncKeyBundle,
                                             cryptoKeys, true);
    if (keysChanged) {
      this._log.info("Downloaded keys differed, as expected.");
    }
  },

  changePassword: function changePassword(newPassword) {
    let client = new UserAPI10Client(this.userAPIURI);
    let cb = Async.makeSpinningCallback();
    client.changePassword(this.identity.username,
                          this.identity.basicPassword, newPassword, cb);

    try {
      cb.wait();
    } catch (ex) {
      this._log.debug("Password change failed: " +
                      CommonUtils.exceptionStr(ex));
      return false;
    }

    // Save the new password for requests and login manager.
    this.identity.basicPassword = newPassword;
    this.persistLogin();
    return true;
  },

  changePassphrase: function changePassphrase(newphrase) {
    return this._catch(function doChangePasphrase() {
      /* Wipe. */
      this.wipeServer();

      this.logout();

      /* Set this so UI is updated on next run. */
      this.identity.syncKey = newphrase;
      this.persistLogin();

      /* We need to re-encrypt everything, so reset. */
      this.resetClient();
      this.collectionKeys.clear();

      /* Login and sync. This also generates new keys. */
      this.sync();

      Svc.Obs.notify("weave:service:change-passphrase", true);

      return true;
    })();
  },

  startOver: function startOver() {
    this._log.trace("Invoking Service.startOver.");
    Svc.Obs.notify("weave:engine:stop-tracking");
    this.status.resetSync();

    // Deletion doesn't make sense if we aren't set up yet!
    if (this.clusterURL != "") {
      // Clear client-specific data from the server, including disabled engines.
      for each (let engine in [this.clientsEngine].concat(this.engineManager.getAll())) {
        try {
          engine.removeClientData();
        } catch(ex) {
          this._log.warn("Deleting client data for " + engine.name + " failed:"
                         + Utils.exceptionStr(ex));
        }
      }
      this._log.debug("Finished deleting client data.");
    } else {
      this._log.debug("Skipping client data removal: no cluster URL.");
    }

    // We want let UI consumers of the following notification know as soon as
    // possible, so let's fake for the CLIENT_NOT_CONFIGURED status for now
    // by emptying the passphrase (we still need the password).
    this._log.info("Service.startOver dropping sync key and logging out.");
    this.identity.resetSyncKey();
    this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
    this.logout();
    Svc.Obs.notify("weave:service:start-over");

    // Reset all engines and clear keys.
    this.resetClient();
    this.collectionKeys.clear();
    this.status.resetBackoff();

    // Reset Weave prefs.
    this._ignorePrefObserver = true;
    Svc.Prefs.resetBranch("");
    this._ignorePrefObserver = false;

    Svc.Prefs.set("lastversion", WEAVE_VERSION);

    this.identity.deleteSyncCredentials();

    // If necessary, reset the identity manager, then re-initialize it so the
    // FxA manager is used.  This is configurable via a pref - mainly for tests.
    let keepIdentity = false;
    try {
      keepIdentity = Services.prefs.getBoolPref("services.sync-testing.startOverKeepIdentity");
    } catch (_) { /* no such pref */ }
    if (keepIdentity) {
      Svc.Obs.notify("weave:service:start-over:finish");
      return;
    }

    this.identity.finalize().then(
      () => {
        this.identity.username = "";
        this.status.__authManager = null;
        this.identity = Status._authManager;
        this._clusterManager = this.identity.createClusterManager(this);
        Svc.Obs.notify("weave:service:start-over:finish");
      }
    ).then(null,
      err => {
        this._log.error("startOver failed to re-initialize the identity manager: " + err);
        // Still send the observer notification so the current state is
        // reflected in the UI.
        Svc.Obs.notify("weave:service:start-over:finish");
      }
    );
  },

  persistLogin: function persistLogin() {
    try {
      this.identity.persistCredentials(true);
    } catch (ex) {
      this._log.info("Unable to persist credentials: " + ex);
    }
  },

  login: function login(username, password, passphrase) {
    function onNotify() {
      this._loggedIn = false;
      if (Services.io.offline) {
        this.status.login = LOGIN_FAILED_NETWORK_ERROR;
        throw "Application is offline, login should not be called";
      }

      let initialStatus = this._checkSetup();
      if (username) {
        this.identity.username = username;
      }
      if (password) {
        this.identity.basicPassword = password;
      }
      if (passphrase) {
        this.identity.syncKey = passphrase;
      }

      if (this._checkSetup() == CLIENT_NOT_CONFIGURED) {
        throw "Aborting login, client not configured.";
      }

      // Ask the identity manager to explicitly login now.
      let cb = Async.makeSpinningCallback();
      this.identity.ensureLoggedIn().then(cb, cb);

      // Just let any errors bubble up - they've more context than we do!
      cb.wait();

      // Calling login() with parameters when the client was
      // previously not configured means setup was completed.
      if (initialStatus == CLIENT_NOT_CONFIGURED
          && (username || password || passphrase)) {
        Svc.Obs.notify("weave:service:setup-complete");
      }
      this._log.info("Logging in the user.");
      this._updateCachedURLs();

      if (!this.verifyLogin()) {
        // verifyLogin sets the failure states here.
        throw "Login failed: " + this.status.login;
      }

      this._loggedIn = true;

      return true;
    }

    let notifier = this._notify("login", "", onNotify.bind(this));
    return this._catch(this._lock("service.js: login", notifier))();
  },

  logout: function logout() {
    // If we failed during login, we aren't going to have this._loggedIn set,
    // but we still want to ask the identity to logout, so it doesn't try and
    // reuse any old credentials next time we sync.
    this._log.info("Logging out");
    this.identity.logout();
    this._loggedIn = false;

    Svc.Obs.notify("weave:service:logout:finish");
  },

  checkAccount: function checkAccount(account) {
    let client = new UserAPI10Client(this.userAPIURI);
    let cb = Async.makeSpinningCallback();

    let username = this.identity.usernameFromAccount(account);
    client.usernameExists(username, cb);

    try {
      let exists = cb.wait();
      return exists ? "notAvailable" : "available";
    } catch (ex) {
      // TODO fix API convention.
      return this.errorHandler.errorStr(ex);
    }
  },

  createAccount: function createAccount(email, password,
                                        captchaChallenge, captchaResponse) {
    let client = new UserAPI10Client(this.userAPIURI);

    // Hint to server to allow scripted user creation or otherwise
    // ignore captcha.
    if (Svc.Prefs.isSet("admin-secret")) {
      client.adminSecret = Svc.Prefs.get("admin-secret", "");
    }

    let cb = Async.makeSpinningCallback();

    client.createAccount(email, password, captchaChallenge, captchaResponse,
                         cb);

    try {
      cb.wait();
      return null;
    } catch (ex) {
      return this.errorHandler.errorStr(ex.body);
    }
  },

  // Stuff we need to do after login, before we can really do
  // anything (e.g. key setup).
  _remoteSetup: function _remoteSetup(infoResponse) {
    let reset = false;

    this._log.debug("Fetching global metadata record");
    let meta = this.recordManager.get(this.metaURL);

    // Checking modified time of the meta record.
    if (infoResponse &&
        (infoResponse.obj.meta != this.metaModified) &&
        (!meta || !meta.isNew)) {

      // Delete the cached meta record...
      this._log.debug("Clearing cached meta record. metaModified is " +
          JSON.stringify(this.metaModified) + ", setting to " +
          JSON.stringify(infoResponse.obj.meta));

      this.recordManager.del(this.metaURL);

      // ... fetch the current record from the server, and COPY THE FLAGS.
      let newMeta = this.recordManager.get(this.metaURL);

      // If we got a 401, we do not want to create a new meta/global - we
      // should be able to get the existing meta after we get a new node.
      if (this.recordManager.response.status == 401) {
        this._log.debug("Fetching meta/global record on the server returned 401.");
        this.errorHandler.checkServerError(this.recordManager.response);
        return false;
      }

      if (!this.recordManager.response.success || !newMeta) {
        this._log.debug("No meta/global record on the server. Creating one.");
        newMeta = new WBORecord("meta", "global");
        newMeta.payload.syncID = this.syncID;
        newMeta.payload.storageVersion = STORAGE_VERSION;
        newMeta.payload.declined = this.engineManager.getDeclined();

        newMeta.isNew = true;

        this.recordManager.set(this.metaURL, newMeta);
        if (!newMeta.upload(this.resource(this.metaURL)).success) {
          this._log.warn("Unable to upload new meta/global. Failing remote setup.");
          return false;
        }
      } else {
        // If newMeta, then it stands to reason that meta != null.
        newMeta.isNew   = meta.isNew;
        newMeta.changed = meta.changed;
      }

      // Switch in the new meta object and record the new time.
      meta              = newMeta;
      this.metaModified = infoResponse.obj.meta;
    }

    let remoteVersion = (meta && meta.payload.storageVersion)?
      meta.payload.storageVersion : "";

    this._log.debug(["Weave Version:", WEAVE_VERSION, "Local Storage:",
      STORAGE_VERSION, "Remote Storage:", remoteVersion].join(" "));

    // Check for cases that require a fresh start. When comparing remoteVersion,
    // we need to convert it to a number as older clients used it as a string.
    if (!meta || !meta.payload.storageVersion || !meta.payload.syncID ||
        STORAGE_VERSION > parseFloat(remoteVersion)) {

      this._log.info("One of: no meta, no meta storageVersion, or no meta syncID. Fresh start needed.");

      // abort the server wipe if the GET status was anything other than 404 or 200
      let status = this.recordManager.response.status;
      if (status != 200 && status != 404) {
        this.status.sync = METARECORD_DOWNLOAD_FAIL;
        this.errorHandler.checkServerError(this.recordManager.response);
        this._log.warn("Unknown error while downloading metadata record. " +
                       "Aborting sync.");
        return false;
      }

      if (!meta)
        this._log.info("No metadata record, server wipe needed");
      if (meta && !meta.payload.syncID)
        this._log.warn("No sync id, server wipe needed");

      reset = true;

      this._log.info("Wiping server data");
      this._freshStart();

      if (status == 404)
        this._log.info("Metadata record not found, server was wiped to ensure " +
                       "consistency.");
      else // 200
        this._log.info("Wiped server; incompatible metadata: " + remoteVersion);

      return true;
    }
    else if (remoteVersion > STORAGE_VERSION) {
      this.status.sync = VERSION_OUT_OF_DATE;
      this._log.warn("Upgrade required to access newer storage version.");
      return false;
    }
    else if (meta.payload.syncID != this.syncID) {

      this._log.info("Sync IDs differ. Local is " + this.syncID + ", remote is " + meta.payload.syncID);
      this.resetClient();
      this.collectionKeys.clear();
      this.syncID = meta.payload.syncID;
      this._log.debug("Clear cached values and take syncId: " + this.syncID);

      if (!this.upgradeSyncKey(meta.payload.syncID)) {
        this._log.warn("Failed to upgrade sync key. Failing remote setup.");
        return false;
      }

      if (!this.verifyAndFetchSymmetricKeys(infoResponse)) {
        this._log.warn("Failed to fetch symmetric keys. Failing remote setup.");
        return false;
      }

      // bug 545725 - re-verify creds and fail sanely
      if (!this.verifyLogin()) {
        this.status.sync = CREDENTIALS_CHANGED;
        this._log.info("Credentials have changed, aborting sync and forcing re-login.");
        return false;
      }

      return true;
    }
    else {
      if (!this.upgradeSyncKey(meta.payload.syncID)) {
        this._log.warn("Failed to upgrade sync key. Failing remote setup.");
        return false;
      }

      if (!this.verifyAndFetchSymmetricKeys(infoResponse)) {
        this._log.warn("Failed to fetch symmetric keys. Failing remote setup.");
        return false;
      }

      return true;
    }
  },

  /**
   * Return whether we should attempt login at the start of a sync.
   *
   * Note that this function has strong ties to _checkSync: callers
   * of this function should typically use _checkSync to verify that
   * any necessary login took place.
   */
  _shouldLogin: function _shouldLogin() {
    return this.enabled &&
           !Services.io.offline &&
           !this.isLoggedIn;
  },

  /**
   * Determine if a sync should run.
   *
   * @param ignore [optional]
   *        array of reasons to ignore when checking
   *
   * @return Reason for not syncing; not-truthy if sync should run
   */
  _checkSync: function _checkSync(ignore) {
    let reason = "";
    if (!this.enabled)
      reason = kSyncWeaveDisabled;
    else if (Services.io.offline)
      reason = kSyncNetworkOffline;
    else if (this.status.minimumNextSync > Date.now())
      reason = kSyncBackoffNotMet;
    else if ((this.status.login == MASTER_PASSWORD_LOCKED) &&
             Utils.mpLocked())
      reason = kSyncMasterPasswordLocked;
    else if (Svc.Prefs.get("firstSync") == "notReady")
      reason = kFirstSyncChoiceNotMade;

    if (ignore && ignore.indexOf(reason) != -1)
      return "";

    return reason;
  },

  sync: function sync() {
    let dateStr = new Date().toLocaleFormat(LOG_DATE_FORMAT);
    this._log.debug("User-Agent: " + SyncStorageRequest.prototype.userAgent);
    this._log.info("Starting sync at " + dateStr);
    this._catch(function () {
      // Make sure we're logged in.
      if (this._shouldLogin()) {
        this._log.debug("In sync: should login.");
        if (!this.login()) {
          this._log.debug("Not syncing: login returned false.");
          return;
        }
      }
      else {
        this._log.trace("In sync: no need to login.");
      }
      return this._lockedSync.apply(this, arguments);
    })();
  },

  /**
   * Sync up engines with the server.
   */
  _lockedSync: function _lockedSync() {
    return this._lock("service.js: sync",
                      this._notify("sync", "", function onNotify() {

      let histogram = Services.telemetry.getHistogramById("WEAVE_START_COUNT");
      histogram.add(1);

      let synchronizer = new EngineSynchronizer(this);
      let cb = Async.makeSpinningCallback();
      synchronizer.onComplete = cb;

      synchronizer.sync();
      // wait() throws if the first argument is truthy, which is exactly what
      // we want.
      let result = cb.wait();

      histogram = Services.telemetry.getHistogramById("WEAVE_COMPLETE_SUCCESS_COUNT");
      histogram.add(1);

      // We successfully synchronized. Now let's update our declined engines.
      let meta = this.recordManager.get(this.metaURL);
      if (!meta) {
        this._log.warn("No meta/global; can't update declined state.");
        return;
      }

      let declinedEngines = new DeclinedEngines(this);
      let didChange = declinedEngines.updateDeclined(meta, this.engineManager);
      if (!didChange) {
        this._log.info("No change to declined engines. Not reuploading meta/global.");
        return;
      }

      this.uploadMetaGlobal(meta);
    }))();
  },

  /**
   * Upload meta/global, throwing the response on failure.
   */
  uploadMetaGlobal: function (meta) {
    this._log.debug("Uploading meta/global: " + JSON.stringify(meta));

    // It would be good to set the X-If-Unmodified-Since header to `timestamp`
    // for this PUT to ensure at least some level of transactionality.
    // Unfortunately, the servers don't support it after a wipe right now
    // (bug 693893), so we're going to defer this until bug 692700.
    let res = this.resource(this.metaURL);
    let response = res.put(meta);
    if (!response.success) {
      throw response;
    }
    this.recordManager.set(this.metaURL, meta);
  },

  /**
   * If we have a passphrase, rather than a 25-alphadigit sync key,
   * use the provided sync ID to bootstrap it using PBKDF2.
   *
   * Store the new 'passphrase' back into the identity manager.
   *
   * We can check this as often as we want, because once it's done the
   * check will no longer succeed. It only matters that it happens after
   * we decide to bump the server storage version.
   */
  upgradeSyncKey: function upgradeSyncKey(syncID) {
    let p = this.identity.syncKey;

    if (!p) {
      return false;
    }

    // Check whether it's already a key that we generated.
    if (Utils.isPassphrase(p)) {
      this._log.info("Sync key is up-to-date: no need to upgrade.");
      return true;
    }

    // Otherwise, let's upgrade it.
    // N.B., we persist the sync key without testing it first...

    let s = btoa(syncID);        // It's what WeaveCrypto expects. *sigh*
    let k = Utils.derivePresentableKeyFromPassphrase(p, s, PBKDF2_KEY_BYTES);   // Base 32.

    if (!k) {
      this._log.error("No key resulted from derivePresentableKeyFromPassphrase. Failing upgrade.");
      return false;
    }

    this._log.info("Upgrading sync key...");
    this.identity.syncKey = k;
    this._log.info("Saving upgraded sync key...");
    this.persistLogin();
    this._log.info("Done saving.");
    return true;
  },

  _freshStart: function _freshStart() {
    this._log.info("Fresh start. Resetting client and considering key upgrade.");
    this.resetClient();
    this.collectionKeys.clear();
    this.upgradeSyncKey(this.syncID);

    // Wipe the server.
    let wipeTimestamp = this.wipeServer();

    // Upload a new meta/global record.
    let meta = new WBORecord("meta", "global");
    meta.payload.syncID = this.syncID;
    meta.payload.storageVersion = STORAGE_VERSION;
    meta.payload.declined = this.engineManager.getDeclined();
    meta.isNew = true;

    // uploadMetaGlobal throws on failure -- including race conditions.
    // If we got into a race condition, we'll abort the sync this way, too.
    // That's fine. We'll just wait till the next sync. The client that we're
    // racing is probably busy uploading stuff right now anyway.
    this.uploadMetaGlobal(meta);

    // Wipe everything we know about except meta because we just uploaded it
    let engines = [this.clientsEngine].concat(this.engineManager.getAll());
    let collections = [engine.name for each (engine in engines)];
    // TODO: there's a bug here. We should be calling resetClient, no?

    // Generate, upload, and download new keys. Do this last so we don't wipe
    // them...
    this.generateNewSymmetricKeys();
  },

  /**
   * Wipe user data from the server.
   *
   * @param collections [optional]
   *        Array of collections to wipe. If not given, all collections are
   *        wiped by issuing a DELETE request for `storageURL`.
   *
   * @return the server's timestamp of the (last) DELETE.
   */
  wipeServer: function wipeServer(collections) {
    let response;
    if (!collections) {
      // Strip the trailing slash.
      let res = this.resource(this.storageURL.slice(0, -1));
      res.setHeader("X-Confirm-Delete", "1");
      try {
        response = res.delete();
      } catch (ex) {
        this._log.debug("Failed to wipe server: " + CommonUtils.exceptionStr(ex));
        throw ex;
      }
      if (response.status != 200 && response.status != 404) {
        this._log.debug("Aborting wipeServer. Server responded with " +
                        response.status + " response for " + this.storageURL);
        throw response;
      }
      return response.headers["x-weave-timestamp"];
    }

    let timestamp;
    for (let name of collections) {
      let url = this.storageURL + name;
      try {
        response = this.resource(url).delete();
      } catch (ex) {
        this._log.debug("Failed to wipe '" + name + "' collection: " +
                        Utils.exceptionStr(ex));
        throw ex;
      }

      if (response.status != 200 && response.status != 404) {
        this._log.debug("Aborting wipeServer. Server responded with " +
                        response.status + " response for " + url);
        throw response;
      }

      if ("x-weave-timestamp" in response.headers) {
        timestamp = response.headers["x-weave-timestamp"];
      }
    }

    return timestamp;
  },

  /**
   * Wipe all local user data.
   *
   * @param engines [optional]
   *        Array of engine names to wipe. If not given, all engines are used.
   */
  wipeClient: function wipeClient(engines) {
    // If we don't have any engines, reset the service and wipe all engines
    if (!engines) {
      // Clear out any service data
      this.resetService();

      engines = [this.clientsEngine].concat(this.engineManager.getAll());
    }
    // Convert the array of names into engines
    else {
      engines = this.engineManager.get(engines);
    }

    // Fully wipe each engine if it's able to decrypt data
    for each (let engine in engines) {
      if (engine.canDecrypt()) {
        engine.wipeClient();
      }
    }

    // Save the password/passphrase just in-case they aren't restored by sync
    this.persistLogin();
  },

  /**
   * Wipe all remote user data by wiping the server then telling each remote
   * client to wipe itself.
   *
   * @param engines [optional]
   *        Array of engine names to wipe. If not given, all engines are used.
   */
  wipeRemote: function wipeRemote(engines) {
    try {
      // Make sure stuff gets uploaded.
      this.resetClient(engines);

      // Clear out any server data.
      this.wipeServer(engines);

      // Only wipe the engines provided.
      if (engines) {
        engines.forEach(function(e) this.clientsEngine.sendCommand("wipeEngine", [e]), this);
      }
      // Tell the remote machines to wipe themselves.
      else {
        this.clientsEngine.sendCommand("wipeAll", []);
      }

      // Make sure the changed clients get updated.
      this.clientsEngine.sync();
    } catch (ex) {
      this.errorHandler.checkServerError(ex);
      throw ex;
    }
  },

  /**
   * Reset local service information like logs, sync times, caches.
   */
  resetService: function resetService() {
    this._catch(function reset() {
      this._log.info("Service reset.");

      // Pretend we've never synced to the server and drop cached data
      this.syncID = "";
      this.recordManager.clearCache();
    })();
  },

  /**
   * Reset the client by getting rid of any local server data and client data.
   *
   * @param engines [optional]
   *        Array of engine names to reset. If not given, all engines are used.
   */
  resetClient: function resetClient(engines) {
    this._catch(function doResetClient() {
      // If we don't have any engines, reset everything including the service
      if (!engines) {
        // Clear out any service data
        this.resetService();

        engines = [this.clientsEngine].concat(this.engineManager.getAll());
      }
      // Convert the array of names into engines
      else {
        engines = this.engineManager.get(engines);
      }

      // Have each engine drop any temporary meta data
      for each (let engine in engines) {
        engine.resetClient();
      }
    })();
  },

  /**
   * Fetch storage info from the server.
   *
   * @param type
   *        String specifying what info to fetch from the server. Must be one
   *        of the INFO_* values. See Sync Storage Server API spec for details.
   * @param callback
   *        Callback function with signature (error, data) where `data' is
   *        the return value from the server already parsed as JSON.
   *
   * @return RESTRequest instance representing the request, allowing callers
   *         to cancel the request.
   */
  getStorageInfo: function getStorageInfo(type, callback) {
    if (STORAGE_INFO_TYPES.indexOf(type) == -1) {
      throw "Invalid value for 'type': " + type;
    }

    let info_type = "info/" + type;
    this._log.trace("Retrieving '" + info_type + "'...");
    let url = this.userBaseURL + info_type;
    return this.getStorageRequest(url).get(function onComplete(error) {
      // Note: 'this' is the request.
      if (error) {
        this._log.debug("Failed to retrieve '" + info_type + "': " +
                        Utils.exceptionStr(error));
        return callback(error);
      }
      if (this.response.status != 200) {
        this._log.debug("Failed to retrieve '" + info_type +
                        "': server responded with HTTP" +
                        this.response.status);
        return callback(this.response);
      }

      let result;
      try {
        result = JSON.parse(this.response.body);
      } catch (ex) {
        this._log.debug("Server returned invalid JSON for '" + info_type +
                        "': " + this.response.body);
        return callback(ex);
      }
      this._log.trace("Successfully retrieved '" + info_type + "'.");
      return callback(null, result);
    });
  },
};

this.Service = new Sync11Service();
Service.onStartup();

redpajama_set_name: RedPajamaGithub


text:
Aimas
Philippine President Rodrigo Duterte's war on drugs had until three weeks ago driven the trade in crystal methamphetamine underground, according to residents and drug users in some of the slum areas of the nation's capital city.
As thousands of users and dealers were shot dead by police and vigilantes in the first seven months after Duterte came to power last June, open dealing in the drug, known here by its street name shabu, largely stopped. Instead, deals were done on the quiet between people who knew each other, maybe with a text message first.
But since Duterte ordered the Philippine National Police (PNP) to stand down from the drugs war last month, after declaring the force "rotten to the core," the drugs trade has come back out of the shadows, more than half a dozen drug users and dealers in some of Manila's toughest areas said in interviews. Many spoke on condition that only their first names be used in this story.
'How much are you going to buy?'
Beside one of the less-used railroad tracks in Manila, a grassy area scattered with human excrement only a few miles from the gleaming high-rises of the Makati business district, shabu was easily available last week, costing just a few pesos (cents) per hit.
Residents said that when they traveled on the illegal trolleys that ferry people for a few pesos along the track when there are no trains in sight, a fellow passenger will often offer them a sachet of the drug.
Eusebio, 52, who pushes a wood and bamboo trolley on the track for a living, said dealers sometimes walk alongside calling out: "How much are you going to buy?"
"Now that the operations have been suspended, drugs have become rampant again," he said. "Those who were hiding have resurfaced."
Another trolley-pusher, Boyser, 59, told two Reuters journalists: "If you weren't reporters, they would offer you drugs."
'Users are still users'
In a dark cinderblock room that serves as a drug den in another part of Manila, there were similar stories from users.
"We have more freedom now," Jason, a 39-year-old bartender told a visiting reporter as he inhaled shabu smoke. "All the users are still users, except those who have been killed," he said, adding that he has used shabu for almost two decades.
More than 8,000 people have been killed since Duterte was sworn in almost eight months ago, about 2,500 of whom were killed in official police anti-narcotics operations. Human rights groups believe many of the others were extra-judicial executions committed as part of the war on drugs, and in cooperation with the police, a claim the Duterte administration has vehemently denied.
The president's office did not respond to a list of emailed questions about the drug war and whether dealers were now openly back on the streets.
Duterte has repeatedly said he will hunt down drug lords and other "high value" targets and there have been a handful of large-scale seizures and raids on shabu laboratories.
But most of those killed in the war on drugs have been small-time dealers and users in some of the country's poorest neighborhoods.
The PNP stopped publishing an official tally of drug war killings from police operations on January 31 when Duterte ordered the Philippine Drug Enforcement Agency (PDEA) to take over the campaign.
Fewer killings
According to reporters and photographers from Reuters and Philippine news organizations working the night shift, "vigilante-style" killings of drug suspects have continued, but at a much slower pace. Police data shows 398 people were killed nationwide in the first 20 days of February.
Details of the killings were not provided and it was unclear how many were drug-related.
Some anti-narcotics experts say they would not be surprised if it turns out that the drug war has been ineffective. They say that ruthless operations against drugs, like Duterte's, have failed elsewhere in the world.
When an aggressive anti-drugs campaign begins, supplies may be tight for a while, street prices may spike, but ultimately drug usage does not drop, say those who have studied the results.
"We don't know of any examples from around the world where very hard-line approaches have worked effectively," said Jeremy Douglas, the regional representative for the United Nations Office on Drugs and Crime. "They can temporarily disrupt street business, but they don't disrupt demand."
Lost momentum
Some police officers told Reuters that they had received reports of increased street-level drug activity since they were ordered to stand down.
Manila Police Commander Olivia Sagaysay, who oversees four precincts in the city, said the war on drugs had lost momentum and morale among her officers had suffered since they were ordered to stand down.
"It's depressing," she said. "But who are we not to follow the higher-ups?"
She said she expected the trade to increase but maybe not return to its previous levels because "networks were disrupted" and "pushers were killed."
In a written response to questions from Reuters about the impact of Duterte's campaign on the street-level shabu trade, the PDEA said that "based on reports gathered, the supply of illegal drugs in some areas are still considerably abundant."
The PDEA attributed low street prices for shabu — prices overall have risen only minimally since the war on drugs began and in some areas have fallen — to a "lack of customers" or drug traffickers trying to get rid of their supply "in order to avoid arrest." It said drugs were being hoarded and that it was difficult for users to transact directly with traffickers. The PDEA did not provide evidence for any of its assessments.
PNP spokesman Dionardo Carlos said drugs would return to the streets because it was "a billion peso business" and "money talks."
In his view, though, the drug war had not failed.
"We hit the target and now it goes back to PDEA. As far as the PNP is concerned we did our part in the past seven months. I hope PDEA will be able to do their part," he said.
The PDEA has just about 1,800 people compared with the national police force of 160,000. Of the existing PDEA personnel, only about half are field operatives.
PDEA spokesman Derrick Carreon said his agency will add staff and that the president would soon be issuing an executive order to set up an anti-illegal drugs inter-agency council and task force that would also draw from the military, the National Bureau of Investigation and the PNP. The task force will be charged with pursuing the war on drugs.
"There is a temporary vacuum of warm bodies but it won't be long," Carreon said, adding that those involved in the drug trade would be wrong to think they were safe.
"If that's their perception, it won't last long," he said. "They will find out in the hardest way that they are terribly wrong."
'Go after cookers'
Still, Jason, the bartender who is a shabu user, said Duterte's campaign was not successful because he targeted the wrong people.
If authorities had gone after the "cookers," the people manufacturing the drugs, instead of users and small dealers, people like him would be unable to buy and would move on. As it is, Jason said, shabu is always in plentiful supply, adding he was addicted and the drug eliminated any fear he may have had of being shot by police or vigilantes.
As he spoke, Jason poured white crystals into a long strip of aluminum foil folded into a trough, tilted it slightly and held a flame below. Almost immediately, it produced a thick white smoke, which he sucked up through a narrow aluminum foil straw.
He then began speaking again, more animatedly. "I buy drugs every day!" he said.
Malaysia Asks Diplomat to Cooperate in Kim Case or Face Arrest
Indonesian President to Visit Australia for Talks on Trade, Security


all right I just a new Telus commercial on television last night .I had this baby chick just run around in the white background. Hey Nate Wright have you seen this commercial. nope I have it seen before .but I forgot about it. I'm looking for the song in the Telus pig commercial .where it's on a skikes in balloons and saying a bad world.
I love that stunt driving! This is my favorite.


The Foxborough Historical Society meets every month September through November and March through May. Our meetings are the 4th Tuesday of the month and will be held at the newly renovated Boyden Library.
Each meeting we have a featured speaker or author who speaks on a particular topic or about his or her latest book. We have even had famous historical figures speak at our meetings such as Eleanor Roosevelt and Mark Twain!
Our treasured town historian Jack Authelet opens and closes each season with must see and learn topics about our wonderful town which is rich in history.
Our monthly programs are free and open to the public.


text:
War Graves Ulster
"Known Unto God"
Acknowledgments for Photographs
First World War (Burials)
Greater Belfast
First World War (Commemorations)
Foreign Nationals (Non CWGC)
Second World War (Burials)
Second World War (Commemorations)
Ballycranbeg (Mount St Joseph) Roman Catholic Graveyard, County Down
Pictures by Nigel Henderson
Polish Airmen Grave
St. Sierz Stanislaw Grandowski, Polish Air Force, died on 11th September 1943, aged 34
Pilot Wladyslaw Kolek, Polish Air Force, died on 11th September 1943, aged 26
PPOR Jerzy Ryszard Tuczemski, Polish Air Force, died on 22nd August 1943, aged 24
Belfast City Cemetery, County Antrim
Pictures provided by Nigel Henderson
Seaman Halvard Halgjem, Norwegian Merchant Navy, died on 6the December 1942, aged 45
Seaman John Jansen, Norwegian Merchant Navy, died on 31st December 1941, aged 29.
Seaman Karl Trygve Karlsen, Norwegian Merchant Navy, died on 20th December 1941, aged 52.
Belfast (Milltown) Roman Catholic Cemetery, County Antrim
Polish Airmen Plot
Airman Klemens Adomowicz, Polish Air Force, died on 20th December 1943, aged 32
Airman Stanislaw Czerniawski, Polish Air Force, died on 20th December 1943, aged 32
Pilot Pawel Kowalewicz, Polish Air Force, died on 20th December 1943, aged 26
Kpl Edward Lewendowski, Polish Air Force, died on 30th April 1942, aged 22
St Szer Kazimierz Lugowski, Polish Air Force, died on 20th December 1943, aged 23
Kpl Wincenti Pietrzak, Polish Air Force, died on 20th December 1943, aged 27
Airman Jozef Antoni Pudelko, Polish Air Force, died on 23rd April 1942, aged 23
SS Otranto-La Croisine Memorial
Memorial to three sailors of the French ship La Croisine who were transferred to HMS Otranto following a mid Atlantic collision. HMS Otranto, carrying American soldiers, collided with HMS Kashmir, off the Irish coast and sank. The three men - Jean Marie Bougeard (died 13th October 1918), Louis Francois Cholon (died 23rd October 1918) and Guillaume Denier (died 14th October 1918) - survived the sinking but subsequently died in hospital in Belfast from complications relating to the Spanish Flu epidemic.
The newspaper picture of the original memorial appeared in the Belfast Telegraph on 2nd November 1921
Carnmoney Jewish Cemetery, County Antrim
Picture provided by Nigel Henderson
Pilot Naftali Hirsz Pawel Kuflik, Polish Forces, died on 20th December 1943, aged 20
Glenavy Roman Catholic Graveyard, County Antrim
Sierz Franciszek Pretkiewicz, Polish Air Force, died on 30th November 1940, aged 26
Faughanvale (St. Canice) Church of Ireland Graveyard, County Londonderry
Pictures provided by Sean S Wilson
2nd Lieutenant Johannes Blok, Royal Netherlands Navy, who died on 27th March 1944 - his remains were later repatriated to Holland.
Seaman Hendrik Christiaan De Jager, Netherlands Navy, died on 18th May 1944
Airman Eric Hendrik Den Hollander, Netherlands Air Force, died on 28th April 1944
Londonderry (Derry) City Cemetery, County Londonderry
Picture provided by Sean S Wilson
Lieutenant C Hos, Royal Dutch Navy, died on 18th July 1942
Newtownards (Movilla) Cemetery, County Down
Pictures provided by Nigel Henderson and Tony Watson
Sierz Henryk Andrzej Flegier, Polish Air Force, died on 5th October 1943, aged 30
PLK Waldyslaw Eugeniusz Heller, Polish Air Force, died on 18th October 1943, aged 48
Kpl Henryk Komenda, Polish Air Force, died on 9th July 1942, aged 22
Rathlin (St Thomas) Church of Ireland Graveyard, County Antrim
Picture provided by Rev. Patrick Barton
Giusseppe Capella, an Italian civilian who died when the SS Arandora Star was sunk in the North Channel on 2nd July 1940 - he was buried on 12th August 1940.
Tullaghobegley Church of Ireland Churchyard, County Donegal
Memorial to three Dutchmen airmen who died on 6th December 1940 - their bodies were repatriated to Holland in October 2000.
Borgman, Eilt Hendrik
Bruyn, Johannes
Hofkens, Antonius Adrianus Theodorus


text:
<?php
/**
 * SimpleXml
 *
 * This class is used as an extension for the functions:
 * - simplexml_load_file
 * - simplexml_load_string
 *
 * It captures errors that occur during parsing and will throw an exception instead of causing a fatal within libxml
 * Also it provides functionality to load an XML from an URL
 *
 * @package SimpleXml
 * @version $Id
 */
class ESL_SimpleXml
{
	/**
	 * Collection of errors that occured
	 *
	 * @var array
	 */
	static protected $aErrors;

	/**
	 * Parse a file to an SimpleXMLElement.
	 *
	 * @throws RuntimeException when the file does not exist, is not readable
	 *
	 * @param string $sFileName
	 * @return SimpleXMLElement
	 */
	static public function loadFile($sFileName)
	{
		// Make sure the file exists.
		if (!file_exists($sFileName)) {
			throw new RuntimeException(sprintf('%s::%s File not found: %s ', __CLASS__, __FUNCTION__, $sFileName));
		}

		// The file must be readable.
		if (!is_readable($sFileName)) {
			throw new RuntimeException(sprintf('%s::%s File not readable: %s ', __CLASS__, __FUNCTION__, $sFileName));
		}

		// Enable user error handling (disables libxml error handling, which can cause fatals)
		libxml_use_internal_errors(true);

		$oSimpleXMLElement = simplexml_load_file($sFileName);

		static::handleErrors();

		// Return SimpleXMLElement.
		return $oSimpleXMLElement;
	}

	/**
	 * Accepts a string and attempts to create a SimpleXMLElement.
	 *
	 * @param string $sXml
	 * @return SimpleXMLElement
	 */
	static public function loadString($sXml)
	{
		// Enable user error handling (disables libxml error handling, which can cause fatals)
		libxml_use_internal_errors(true);

		$oSimpleXMLElement = simplexml_load_string($sXml);

		static::handleErrors();

		// Return SimpleXMLElement.
		return $oSimpleXMLElement;
	}

	/**
	 * Accepts an URL as resource and attempts to load it in a SimpleXMLElement.
	 *
	 * @throws RuntimeException when the url is not available.
	 * @throws InvalidArgumentException when given location is not a valid URL
	 *
	 * @param string $sXmlLocation
	 * @return SimpleXMLElement
	 */
	static public function loadUrl($sXmlLocation)
	{
		$aUrlParts = parse_url($sXmlLocation);

		if (!is_array($aUrlParts)) {
			throw new InvalidArgumentException('Invalid resource, ' . $sXmlLocation . ' is not a valid URL');
		}

		$mHandler = curl_init($sXmlLocation);

		if (!is_resource($mHandler)) {
			throw new RuntimeException('Unable to load XML from ' . $sXmlLocation);
		}

		curl_setopt($mHandler, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($mHandler, CURLOPT_HEADER, false);

		$sResponse = curl_exec($mHandler);
		curl_close($mHandler);

		if ($sResponse === false) {
			throw new RuntimeException('Unable to load XML from ' . $sXmlLocation);
		}

		// Use the content to create a simplexmlelement.
		return static::loadString($sResponse);
	}

	/**
	 * Internal method to handle libXML errors in case an invalid XML is parsed. It will also
	 * set the errorhandling back to default (libxml error handling). Optional found erros can
	 * be retrieved with getErrors method
	 *
	 * @throws RuntimeException When invalid XML is parsed
	 */
	static protected function handleErrors()
	{
		static::$aErrors = libxml_get_errors();

		// Disable user error handling (enables libxml error handling, back to default)
		libxml_use_internal_errors(false);

		if (!empty(static::$aErrors)) {
			/*
				@todo Throw usefull exception
				$oLibXmlError = reset(static::$aErrors);
				$sMessage = sprintf('%s in file %s:%d (column %d)', trim($oLibXmlError->message), $oLibXmlError->file, $oLibXmlError->line, $oLibXmlError->column);
				throw new RuntimeException($sMessage, $oLibXmlError->code);
			*/
			// Enable use internal errors.
			throw new RuntimeException(sprintf('%s::%s Invalid XML string', __CLASS__, __FUNCTION__));
		}
	}

	/**
	 * Returns array of found XML errors during parsing
	 *
	 * @return array
	 */
	static public function getErrors()
	{
		return static::$aErrors;
	}
}
?>


text:
Hilma: En roman om gåtan Hilma af Klint by Anna Laestadius Larsson
Stockholm. Piratförlaget. 2017. 323 pages.
Seeing to believing: it could be the motto for this book. Hilma, "A novel about the enigma that was Hilma af Klint," is a captivating, intelligently fictionalized "life" of a painter who has only recently become internationally famous, but it seems hard to engage with the character Hilma af Klint. Her fervent—and, yes, enigmatic—psychic beliefs dominate her story, increasingly taking over from what we instinctively warm to as "normal" preoccupations. But, seeing her paintings, manifestations of another world perceived by her alone, is not only an overwhelming experience but also imparts meaning to her as a character and as a person.
Hilma af Klint was born in 1862 into a comfortable upper-middle-class family. She was well educated for a girl of that time and class; her father had enchanted her with mathematical games and stories of scientific advances ever since she was a clever, fey child. She read On the Origin of Species while still a schoolgirl, partly because she found Darwin's theories a good way to stop the spirit of her dead older sister, Anna, from spooking her. For the rest of Hilma's life, her art was inspired by the tensions between our biological existence and the spiritual messages flowing into her mind, mostly in a quasireligious flow of high-toned moral instruction.
A young woman at a time when the barriers against women doing anything other than getting and being married were breaking down, Hilma was eventually allowed to study art seriously. During a period as a struggling painter of landscapes and portraits, she became part of The Five, a group of women with similar interests and backgrounds. The group served as an emotional hothouse that encouraged Hilma's few love affairs—all with other women—as well as her growing fascination with esoteric knowledge. The most extraordinary part of her life as an artist followed from a gradual, instinctive rejection of the demands of bodily pleasures, from food to love, until she had eschewed just about everything but contemplation of nature (late in life, she studied mosses and flowers with a botanist's focus on detail) and painting. af Klint would have understood what her contemporary and potential soulmate Kandinsky meant by seeking "to symbolize the innerer Klang or inner need of the artist." Spiritual guidance drove her to express transcendent truths in abstract shapes and glowing colors on very large canvases—the extraordinary Temple series alone contains almost two hundred of these. Text, including spirit "words," as well as elements of mathematics and biology are included in the images.
Anna Laestadius Larsson deals ingeniously with the challenge of portraying the woman as well as her work by creating a frame story: eighty-two-year-old Hilma tells her grown-up nephew Erik about her life. We gain insights into her steadiness of purpose and personal magnetism; Erik is deeply impressed both by his aunt and her art, but—like so many of us—confused by the talk of spirits. Hilma's storytelling explains a great deal about her, including her fundamental lack of interest in other people.
Long extracts from contemporary records, including Hilma's own, are inserted to inform us about the rituals of The Five and, later, the role of the Theosophists. Laestadius Larsson comes across as an empathetic, gentle narrator, especially when Hilma and her female friends yet again come up against the restrictions of an unequivocally male-dominated society. The historical reality that surrounded these men and women is carefully and illuminatingly drawn.
af Klint, who ruthlessly burned all her private papers, tried to express her convictions in writing for posterity. She filled notebooks with drawings and commentaries and wrote a 2,000-page manuscript entitled "Studies of the Life of the Soul." Until her paintings suddenly emerged into the international limelight, all these efforts to make herself understood were comprehensively ignored as were her paintings, although partly at her own insistence. As Laestadius Larsson shows, the reception for abstract art was angry and resentful during af Klint's lifetime. Her work, she declared, was to be kept under wraps until at least twenty years after her death; it took about fifty years before she was recognized as the remarkable artist she is.
Anna Paterson
House of Glack, UK