error - Dave's Blog

Search
My timeline on Mastodon

Windows.Web.UI.Interop.WebViewControl localhost access

2018 Jul 25, 5:34

If you're developing with the new Windows.Web.UI.Interop.WebViewControl you may have noticed you cannot navigate to localhost HTTP servers. This is because the WebViewControl's WebView process is a UWP process. All UWP processes by default cannot use the loopback adapter as a security precaution. For development purposes you can allow localhost access using the checknetisolation command line tool on the WebViewControl's package just as you can for any other UWP app. The command should be the following:

checknetisolation loopbackexempt -a -n=Microsoft.Win32WebViewHost_cw5n1h2txyewy

As a warning checknetisolation is not good on errors. If you attempt to add a package but get its package family name wrong, checknetisolation just says OK:

C:\Users\davris>checknetisolation LoopbackExempt -a -n=Microsoft.BingWeather_4.21.2492.0_x86__8wekyb3d8bbwe
OK.
And if you then list the result of the add with the bad name you'll see the following:
[1] -----------------------------------------------------------------
Name: AppContainer NOT FOUND
SID: S-1-15-...

There's also a UI tool for modifying loopback exemption for packages available on GitHub and also one available with Fiddler.

As an additional note, I mentioned above you can try this for development. Do not do this in shipping products as this turns off the security protection for any consumer of the WebViewControl.

PermalinkCommentschecknetisolation loopback security uwp webview win32webview

MSApp.getHtmlPrintDocumentSourceAsync - JavaScript UWP app printing

2017 Oct 11, 5:49

The documentation for printing in JavaScript UWP apps is out of date as it all references MSApp.getHtmlPrintDocumentSource but that method has been replaced by MSApp.getHtmlPrintDocumentSourceAsync since WinPhone 8.1.

Background

Previous to WinPhone 8.1 the WebView's HTML content ran on the UI thread of the app. This is troublesome for rendering arbitrary web content since in the extreme case the JavaScript of some arbitrary web page might just sit in a loop and never return control to your app's UI. With WinPhone 8.1 we added off thread WebView in which the WebView runs HTML content on a separate UI thread.

Off thread WebView required changing our MSApp.getHtmlPrintDocumentSource API which could no longer synchronously produce an HtmlPrintDocumentSource. With WebViews running on their own threads it may take some time for them to generate their print content for the HtmlPrintDocumentSource and we don't want to hang the app's UI thread in the interim. So the MSApp.getHtmlPrintDocumentSource API was replaced with MSApp.getHtmlPrintDocumentSourceAsync which returns a promise the resolved value of which is the eventual HtmlPrintDocumentSource.

Sample

However, the usage of the API is otherwise unchanged. So in sample code you see referencing MSApp.getHtmlPrintDocumentSource the sample code is still reasonable but you need to call MSApp.getHtmlPrintDocumentSourceAsync instead and wait for the promise to complete. For example the PrintManager docs has an example implementing a PrintTaskRequested event handler in a JavaScript UWP app.

    function onPrintTaskRequested(printEvent) {    
var printTask = printEvent.request.createPrintTask("Print Sample", function (args) {
args.setSource(MSApp.getHtmlPrintDocumentSource(document));
});

Instead we need to obtain a deferral in the event handler so we can asynchronously wait for getHtmlPrintDocumentSourceAsync to complete:

    function onPrintTaskRequested(printEvent) {    
var printTask = printEvent.request.createPrintTask("Print Sample", function (args) {
const deferral = args.getDeferral();
MSApp.getHtmlPrintDocumentSourceAsync(document).then(htmlPrintDocumentSource => {
args.setSource(htmlPrintDocumentSource);
deferral.complete();
}, error => {
console.error("Error: " + error.message + " " + error.stack);
deferral.complete();
});
});
PermalinkCommentsjavascript MSApp printing programming uwp webview win10 windows

Win10 UWP WebView AddWebAllowedObject details

2017 Sep 4, 3:09

The x-ms-webview HTML element has the void addWebAllowedObject(string name, any value) method and the webview XAML element has the void AddWebAllowedObject(String name, Object value) method. The object parameter is projected into the webview’s top-level HTML document’s script engine as a new property on the global object with property name set to the name parameter. It is not injected into the current document but rather it is projected during initialization of the next top-level HTML document to which the webview navigates.

Lifetime

If AddWebAllowedObject is called during a NavigationStarting event handler the object will be injected into the document resulting from the navigation corresponding to that event.

If AddWebAllowedObject is called outside of the NavigationStarting event handler it will apply to the navigation corresponding to the next explicit navigate method called on the webview or the navigation corresponding to the next NavigationStarting event handler that fires, whichever comes first.

To avoid this potential race, you should use AddWebAllowedObject in one of two ways: 1. During a NavigationStarting event handler, 2. Before calling a Navigate method and without returning to the main loop.

If called both before calling a navigate method and in the NavigationStarting event handler then the result is the aggregate of all those calls.

If called multiple times for the same document with the same name the last call wins and the previous are silently ignored.

If AddWebAllowedObject is called for a navigation and that navigation fails or redirects to a different URI, the AddWebAllowedObject call is silently ignored.

After successfully adding an object to a document, the object will no longer be projected once a navigation to a new document occurs.

WinRT access

If AddWebAllowedObject is called for a document with All WinRT access then projection will succeed and the object will be added.

If AddWebAllowedObject is called for a document which has a URI which has no declared WinRT access via ApplicationContentUriRules then Allow for web only WinRT access is given to that document.

If the document has Allow for web only WinRT access then projection will succeed only if the object’s runtimeclass has the Windows.Foundation.Metadata.AllowForWeb metadata attribute.

Object requirements

The object must implement the IAgileObject interface. Because the XAML and HTML webview elements run on ASTA view threads and the webview’s content’s JavaScript thread runs on another ASTA thread a developer should not create their non-agile runtimeclass on the view thread. To encourage end developers to do this correctly we require the object implements IAgileObject.

Property name

The name parameter must be a valid JavaScript property name, otherwise the call will fail silently. If the name is already a property name on the global object, that property is overwritten if the property is configurable. Non-configurable properties on the global object are not overwritten and the AddWebAllowedObject call fails silently. On success, the projected property is writable, configurable, and enumerable.

Errors

Some errors as described above fail silently. Other issues, such as lack of IAgileObject or lack of the AllowForWeb attribute result in an error in the JavaScript developer console.

PermalinkComments

JavaScript Microsoft Store app StartPage

2017 Jun 22, 8:58

JavaScript Microsoft Store apps have some details related to activation that are specific to JavaScript Store apps and that are poorly documented which I’ll describe here.

StartPage syntax

The StartPage attributes in the AppxManifest.xml (Package/Applications/Application/@StartPage, Package/Applications/Extensions/Extension/@StartPage) define the HTML page entry point for that kind of activation. That is, Application/@StartPage defines the entry point for tile activation, Extension[@Category="windows.protocol"]/@StartPage defines the entry point for URI handling activation, etc. There are two kinds of supported values in StartPage attributes: relative Windows file paths and absolute URIs. If the attribute doesn’t parse as an absolute URI then it is instead interpreted as relative Windows file path.

This implies a few things that I’ll declare explicitly here. Windows file paths, unlike URIs, don’t have a query or fragment, so if you are using a relative Windows file path for your StartPage attribute you cannot include anything like ‘?param=value’ at the end. Absolute URIs use percent-encoding for reserved characters like ‘%’ and ‘#’. If you have a ‘#’ in your HTML filename then you need to percent-encode that ‘#’ for a URI and not for a relative Windows file path.

If you specify a relative Windows file path, it is turned into an ms-appx URI by changing all backslashes to forward slashes, percent-encoding reserved characters, and combining the result with a base URI of ms-appx:///. Accordingly the relative Windows file paths are relative to the root of your package. If you are using a relative Windows file path as your StartPage and need to switch to using a URI so you can include a query or fragment, you can follow the same steps above.

StartPage validity

The validity of the StartPage is not determined before activation. If the StartPage is a relative Windows file path for a file that doesn’t exist, or an absolute URI that is not in the Application Content URI Rules, or something that doesn’t parse as a Windows file path or URI, or otherwise an absolute URI that fails to resolve (404, bad hostname, etc etc) then the JavaScript app will navigate to the app’s navigation error page (perhaps more on that in a future blog post). Just to call it out explicitly because I have personally accidentally done this: StartPage URIs are not automatically included in the Application Content URI Rules and if you forget to include your StartPage in your ACUR you will always fail to navigate to that StartPage.

StartPage navigation

When your app is activated for a particular activation kind, the StartPage value from the entry in your app’s manifest that corresponds to that activation kind is used as the navigation target. If the app is not already running, the app is activated, navigated to that StartPage value and then the Windows.UI.WebUI.WebUIApplication activated event is fired (more details on the order of various events in a moment). If, however, your app is already running and an activation occurs, we navigate or don’t navigate to the corresponding StartPage depending on the current page of the app. Take the app’s current top level document’s URI and if after removing the fragment it already matches the StartPage value then we won’t navigate and will jump straight to firing the WebUIApplication activated event.

Since navigating the top-level document means destroying the current JavaScript engine instance and losing all your state, this behavior might be a problem for you. If so, you can use the MSApp.pageHandlesAllApplicationActivations(true) API to always skip navigating to the StartPage and instead always jump straight to firing the WebUIApplication activated event. This does require of course that all of your pages all handle all activation kinds about which any part of your app cares.

PermalinkComments

Tweet from Gregory Schier

2016 May 17, 5:11
Thanks @elmlang for the most useful error message I've ever seen
PermalinkComments

Tweet from David_Risney

2016 Feb 17, 3:30
Well done, tense, fake Back to the Future prequel trailer: http://www.avclub.com/article/tense-prequel-fully-explores-back-futures-terroris-232391 …. Doc must trick terrorists into giving him plutonium.
PermalinkComments

JavaScript Types and WinRT Types

2016 Jan 21, 5:35

MSDN covers the topic of JavaScript and WinRT type conversions provided by Chakra (JavaScript Representation of Windows Runtime Types and Considerations when Using the Windows Runtime API), but for the questions I get about it I’ll try to lay out some specifics of that discussion more plainly. I’ve made a TL;DR JavaScript types and WinRT types summary table.

WinRT Conversion JavaScript
Struct ↔️ JavaScript object with matching property names
Class or interface instance JavaScript object with matching property names
Windows.Foundation.Collections.IPropertySet JavaScript object with arbitrary property names
Any DOM object

Chakra, the JavaScript engine powering the Edge browser and JavaScript Windows Store apps, does the work to project WinRT into JavaScript. It is responsible for, among other things, converting back and forth between JavaScript types and WinRT types. Some basics are intuitive, like a JavaScript string is converted back and forth with WinRT’s string representation. For other basic types check out the MSDN links at the top of the page. For structs, interface instances, class instances, and objects things are more complicated.

A struct, class instance, or interface instance in WinRT is projected into JavaScript as a JavaScript object with corresponding property names and values. This JavaScript object representation of a WinRT type can be passed into other WinRT APIs that take the same underlying type as a parameter. This JavaScript object is special in that Chakra keeps a reference to the underlying WinRT object and so it can be reused with other WinRT APIs.

However, if you start with plain JavaScript objects and want to interact with WinRT APIs that take non-basic WinRT types, your options are less plentiful. You can use a plain JavaScript object as a WinRT struct, so long as the property names on the JavaScript object match the WinRT struct’s. Chakra will implicitly create an instance of the WinRT struct for you when you call a WinRT method that takes that WinRT struct as a parameter and fill in the WinRT struct’s values with the values from the corresponding properties on your JavaScript object.

// C# WinRT component
public struct ExampleStruct
{
public string String;
public int Int;
}

public sealed class ExampleStructContainer
{
ExampleStruct value;
public void Set(ExampleStruct value)
{
this.value = value;
}

public ExampleStruct Get()
{
return this.value;
}
}

// JS code
var structContainer = new ExampleWinRTComponent.ExampleNamespace.ExampleStructContainer();
structContainer.set({ string: "abc", int: 123 });
console.log("structContainer.get(): " + JSON.stringify(structContainer.get()));
// structContainer.get(): {"string":"abc","int":123}

You cannot have a plain JavaScript object and use it as a WinRT class instance or WinRT interface instance. Chakra does not provide such a conversion even with ES6 classes.

You cannot take a JavaScript object with arbitrary property names that are unknown at compile time and don’t correspond to a specific WinRT struct and pass that into a WinRT method. If you need to do this, you have to write additional JavaScript code to explicitly convert your arbitrary JavaScript object into an array of property name and value pairs or something else that could be represented in WinRT.

However, the other direction you can do. An instance of a Windows.Foundation.Collections.IPropertySet implementation in WinRT is projected into JavaScript as a JavaScript object with property names and values corresponding to the key and value pairs in the IPropertySet. In this way you can project a WinRT object as a JavaScript object with arbitrary property names and types. But again, the reverse is not possible. Chakra will not convert an arbitrary JavaScript object into an IPropertySet.

// C# WinRT component
public sealed class PropertySetContainer
{
private Windows.Foundation.Collections.IPropertySet otherValue = null;

public Windows.Foundation.Collections.IPropertySet other
{
get
{
return otherValue;
}
set
{
otherValue = value;
}
}
}

public sealed class PropertySet : Windows.Foundation.Collections.IPropertySet
{
private IDictionary map = new Dictionary();

public PropertySet()
{
map.Add("abc", "def");
map.Add("ghi", "jkl");
map.Add("mno", "pqr");
}
// ... rest of PropertySet implementation is simple wrapper around the map member.


// JS code
var propertySet = new ExampleWinRTComponent.ExampleNamespace.PropertySet();
console.log("propertySet: " + JSON.stringify(propertySet));
// propertySet: {"abc":"def","ghi":"jkl","mno":"pqr"}

var propertySetContainer = new ExampleWinRTComponent.ExampleNamespace.PropertySetContainer();
propertySetContainer.other = propertySet;
console.log("propertySetContainer.other: " + JSON.stringify(propertySetContainer.other));
// propertySetContainer.other: {"abc":"def","ghi":"jkl","mno":"pqr"}

try {
propertySetContainer.other = { "123": "456", "789": "012" };
}
catch (e) {
console.error("Error setting propertySetContainer.other: " + e);
// Error setting propertySetContainer.other: TypeError: Type mismatch
}

There’s also no way to implicitly convert a DOM object into a WinRT type. If you want to write third party WinRT code that interacts with the DOM, you must do so indirectly and explicitly in JavaScript code that is interacting with your third party WinRT. You’ll have to extract the information you want from your DOM objects to pass into WinRT methods and similarly have to pass messages out from WinRT that say what actions the JavaScript should perform on the DOM.

PermalinkCommentschakra development javascript winrt

Retweet of SwiftOnSecurity

2015 Nov 15, 11:34
@snowden please stop teaching terrorists how to encrypt their communications with PlayStation 4
PermalinkComments

Retweet of mfeathers

2015 Apr 8, 5:44
I have no words (C++) http://tgceec.tumblr.com/post/74534916370/results-of-the-grand-c-error-explosion …
PermalinkComments

Debugging anecdote - the color transparent black breaks accessibility

2014 May 22, 10:36

Some time back while I was working on getting the Javascript Windows Store app platform running on Windows Phone (now available on the last Windows Phone release!) I had an interesting bug that in retrospect is amusing.

I had just finished a work item to get accessibility working for JS WinPhone apps when I got a new bug: With some set of JS apps, accessibility appeared to be totally broken. At that time in development the only mechanism we had to test accessibility was a test tool that runs on the PC, connects to the phone, and dumps out the accessibility tree of whatever app is running on the phone. In this bug, the tool would spin for a while and then timeout with an error and no accessibility information.

My first thought was this was an issue in my new accessibility code. However, debugging with breakpoints on my code I could see none of my code was run nor the code that should call it. The code that called that code was a more generic messaging system that hit my breakpoints constantly.

Rather than trying to work backward from the failure point, I decided to try and narrow down the repro and work forwards from there. One thing all the apps with the bug had in common was their usage of WinJS, but not all WinJS apps demonstrated the issue. Using a binary search approach on one such app I removed unrelated app code until all that was left was the app's usage of the WinJS AppBar and the bug still occurred. I replaced the WinJS AppBar usage with direct usage of the underlying AppBar WinRT APIs and continued.

Only some calls to the AppBar WinRT object produced the issue:

        var appBar = Windows.UI.WebUI.Core.WebUICommandBar.getForCurrentView(); 
// appBar.opacity = 1;
// appBar.closeDisplayMode = Windows.UI.WebUI.Core.WebUICommandBarClosedDisplayMode.default;
appBar.backgroundColor = Windows.UI.Colors.white; // Bug!
Just setting the background color appeared to cause the issue and I didn't even have to display the AppBar. Through additional trial and error I was blown away to discover that some colors I would set caused the issue and other colors did not. Black wouldn't cause the issue but transparent black would. So would aqua but not white.

I eventually realized that predefined WinRT color values like Windows.UI.Colors.aqua would cause the issue while JS literal based colors didn't cause the issue (Windows.UI.Color is a WinRT struct which projects in JS as a JS literal object with the struct members as JS object properties so its easy to write something like {r: 0, g: 0, b: 0, a: 0} to make a color) and I had been mixing both in my tests without realizing there would be a difference. I debugged into the backgroundColor property setter that consumed the WinRT color struct to see what was different between Windows.UI.Colors.black and {a: 1, r: 0, g: 0, b: 0} and found the two structs to be byte wise exactly the same.

On a hunch I tried my test app with only a reference to the color and otherwise no interaction with the AppBar and not doing anything with the actual reference to the color: Windows.UI.Colors.black;. This too caused the issue. I knew that the implementation for these WinRT const values live in a DLL and guessed that something in the code to create these predefined colors was causing the issue. I debugged in and no luck. Now I also have experienced crusty code that would do exciting things in its DllMain, the function that's called when a DLL is loaded into the process so I tried modifying my C++ code to simply LoadLibrary the DLL containing the WinRT color definition, windows.ui.xaml.dll and found the bug still occurred! A short lived moment of relief as the world seemed to make sense again.

Debugging into DllMain nothing interesting happened. There were interesting calls in there to be sure, but all of them behind conditions that were false. I was again stumped. On another hunch I tried renaming the DLL and only LoadLibrary'ing it and the bug went away. I took a different DLL renamed it windows.ui.xaml.dll and tried LoadLibrary'ing that and the bug came back. Just the name of the DLL was causing the issue.

I searched for the DLL name in our source code index and found hits in the accessibility tool. Grinning I opened the source to find that the accessibility tool's phone side service was trying to determine if a process belonged to a XAML app or not because XAML apps had a different accessibility contract. It did this by checking to see if windows.ui.xaml.dll was loaded in the target process.

At this point I got to fix my main issue and open several new bugs for the variety of problems I had just run into. This is a how to on writing software that is difficult to debug.

PermalinkCommentsbug debug javascript JS technical windows winrt

Results of the Grand C++ Error Explosion Competition

2014 Jan 28, 4:58

tgceec:

After much deliberation, the winners of the Grand C++ Error Explosion Competition are finally selected. There are two different award categories. The winners of the first category are those submissions that produced the largest error with the smallest amount of source code. These entries contain a…

PermalinkCommentshumor technical c++ programming coding

FitBit and WebOC Application Compatibility Errors

2013 Aug 29, 7:17
I just got a FitBit One from my wife. Unfortunately I had issues running their app on my Windows 8.1 Preview machine. But I recognized the errors as IE compatibility issues, for instance an IE dialog popup from the FitBit app telling me about an error in the app's JavaScript. Given my previous post on WebOC versioning you may guess what I tried next. I went into the registry and tried out different browser mode and document mode versions until I got the FitBit software running without error. Ultimately I found the following registry value to work well ('FitBit connect.exe' set to DWORD decimal 8888).
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION]
"Fitbit Connect.exe"=dword:000022b8

For those familiar with the Windows registry the above should be enough. For those not familiar, copy and paste the above into notepad, save as a file named "fitbit.reg", and then double click the reg file and say 'Yes' to the prompt. Hopefully in the final release of Windows 8.1 this won't be an issue.
PermalinkComments

MSVC++ 64bit Enums

2013 Jul 1, 1:00

If you want to represent a value larger than 32bits in an enum in MSVC++ you can use C++0x style syntax to tell the compiler exactly what kind of integral type to store the enum values. Unfortunately by default an enum is always 32bits, and additionally while you can specify constants larger than 32bits for the enum values, they are silently truncated to 32bits.

For instance the following doesn't compile because Lorem::a and Lorem::b have the same value of '1':


enum Lorem {
a = 0x1,
b = 0x100000001
} val;

switch (val) {
case Lorem::a:
break;
case Lorem::b:
break;
}

Unfortunately it is not an error to have b's constant truncated, and the previous without the switch statement does compile just fine:


enum Lorem {
a = 0x1,
b = 0x100000001
} val;

But you can explicitly specify that the enum should be represented by a 64bit value and get expected compiling behavior with the following:


enum Lorem : UINT64 {
a = 0x1,
b = 0x100000001
} val;

switch (val) {
case Lorem::a:
break;
case Lorem::b:
break;
}
PermalinkComments64bit c++ development enum msvc++ technical

In Depth Review: New NSA Documents Expose How Americans Can Be Spied on Without A Warrant

2013 Jun 21, 10:43

What It All Means: All Your Communications are Belong to U.S. In sum, if you use encryption they’ll keep your data forever. If you use Tor, they’ll keep your data for at least five years. If an American talks with someone outside the US, they’ll keep your data for five years. If you’re talking to your attorney, you don’t have any sense of privacy. And the NSA can hand over you information to the FBI for evidence of any crime, not just terrorism. All without a warrant or even a specific FISA order.

Not sure if this is saying all Tor data is collected or saying if someone uses Tor then start collecting that someone’s communication.

PermalinkCommentstechnical legal tor nsa eff spying security privacy

Attention:!!!, Behold, you are reading a letter from your President Barack Obama.

2012 Sep 26, 2:43

Eric gets the most entertaining mail.

You have failed to comply with them after all the warning and instructions given to you, but since you are also among the terrorist we are facing in the country, I will personal make sure that I wipe away the crime in the state and I promise you that you will definitely pay with your life because I am here to protect the interest of my people and not to put them in shame, you suppose to support this government and not to spoil it.

PermalinkCommentshumor spam scam email eric-law

Indicating Details of Problems to Machines in HTTP

2012 Jul 3, 2:28

This specification defines a “Problem Detail” as an extensible way to carry machine-readable details of errors in a response, to avoid the need to invent new response formats.

PermalinkCommentstechnical http ietf standard

FuckItJS

2012 Jun 22, 9:19

jQuery plugin that blindly removes lines with errors and recompiles until it works  

PermalinkCommentstechnical humor javascript programming coding jquery

Image Error Level Analysis with HTML5

2012 Apr 16, 1:59

Javascript tool says if a photo is shopped. It can tell by looking at the pixels. Seriously. Links to cool presentation on the theory behind the algorithm behind the tool: http://www.wired.com/images_blogs/threatlevel/files/bh-usa-07-krawetz.pdf

PermalinkCommentstechnical javascript jpeg photoshop

Client Side Cross Domain Data YQL Hack

2012 Feb 27, 2:28

One of the more limiting issues of writing client side script in the browser is the same origin limitations of XMLHttpRequest. The latest version of all browsers support a subset of CORS to allow servers to opt-in particular resources for cross-domain access. Since IE8 there's XDomainRequest and in all other browsers (including IE10) there's XHR L2's cross-origin request features. But the vast majority of resources out on the web do not opt-in using CORS headers and so client side only web apps like a podcast player or a feed reader aren't doable.

One hack-y way around this I've found is to use YQL as a CORS proxy. YQL applies the CORS header to all its responses and among its features it allows a caller to request an arbitrary XML, HTML, or JSON resource. So my network helper script first attempts to access a URI directly using XDomainRequest if that exists and XMLHttpRequest otherwise. If that fails it then tries to use XDR or XHR to access the URI via YQL. I wrap my URIs in the following manner, where type is either "html", "xml", or "json":

        yqlRequest = function(uri, method, type, onComplete, onError) {
var yqlUri = "http://query.yahooapis.com/v1/public/yql?q=" +
encodeURIComponent("SELECT * FROM " + type + ' where url="' + encodeURIComponent(uri) + '"');

if (type == "html") {
yqlUri += encodeURIComponent(" and xpath='/*'");
}
else if (type == "json") {
yqlUri += "&callback=&format=json";
}
...

This also means I can get JSON data itself without having to go through JSONP.
PermalinkCommentsxhr javascript yql client-side technical yahoo xdr cors

Prime HTTP Status Codes

2012 Feb 22, 4:00
These are the prime HTTP status codes:
PermalinkCommentshttp prime technical useless
Older Entries Creative Commons License Some rights reserved.