app-manager/fetch.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Jul 16 11:07:18 2016 +0100 (2016-07-16)
changeset 61 31fb35727621
parent 14 29d8bb64056c
permissions -rw-r--r--
Support parallel installations. The idea is that for CAD screener, we want
to be able to install this on the same machine as a standard AVOT setup
(most notably for John's laptop). To allow for the possibility of a second
application that might have the same requirements, we add the concept of
vendor-specific distributions. Thus we can have one distribution for CAD
screener and one for The Next Big Thing. It doesn't seem trivial to have
both CAD screener and AVOT under the same vendor tag so we'll have to have
AVOT under "City Occupational" and CAD screener under "City Occupational Ltd"
or some such kludge.

Most of this is done although we are very short of test cases (in particular
we don't test that it's actually possible to install CAD screener in parallel
with AVOT or to update either of them once installed, which is fundamental).

We also have a lot of baggage left over, including an intercept of razor_set.
The problem that this was introduced to debug has been fixed but it looks
like there are a number of memory leaks which it might be useful to help
track down so it has been left in place for now.

There is still a lot of confusion in plover between path-based and URI-based
API. We should review the API, decide what we want and have a general clear up.

There is also confusion as to the purpose of RAZOR_ROOT (and meaning; path or
URI). This is not used at all in librazor (although it is used in razor.exe).
Ideally we shouldn't use it in plover or plover-gtk either although again, we
might want to support it or an equivalent in (some of) the various executables.

Work that would still to nice to do for CAD screener:

- uninstall (ideally as an installed program that hooks into Add/Remove programs
but even re-running the installer would be acceptable).
- xz support (smaller packages).
- repomd.xml and xml:base (would be needed for an Internet installer).
- graphical installer.
     1 /*
     2  * A program to explore http support on MS-Windows.
     3  */
     4 
     5 #include "config.h"
     6 #include <stdlib.h>
     7 #include <stdio.h>
     8 #include <glib.h>
     9 #include <razor.h>
    10 #if HAVE_WINHTTP_H
    11 #include <windows.h>
    12 #include <winhttp.h>
    13 #include <wincrypt.h>
    14 
    15 #ifndef PKCS12_NO_PERSIST_KEY
    16 #define PKCS12_NO_PERSIST_KEY 0x00008000
    17 #endif
    18 
    19 static gboolean debug=FALSE;
    20 
    21 static GOptionEntry entries[] = 
    22 {
    23     { "debug",'d',0,G_OPTION_ARG_NONE,&debug,"Output debugging",NULL },
    24     { NULL }
    25 };
    26 
    27 static struct WinHttpFuncs
    28 {
    29     WINBOOL (WINAPI *AddRequestHeaders)(HINTERNET,LPCWSTR,DWORD,DWORD);
    30     WINBOOL (WINAPI *DetectAutoProxyConfigUrl)(DWORD,LPWSTR*);
    31     WINBOOL (WINAPI *CheckPlatform)(void);
    32     WINBOOL (WINAPI *CloseHandle)(HINTERNET);
    33     HINTERNET (WINAPI *Connect)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD);
    34     WINBOOL (WINAPI *CrackUrl)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS);
    35     WINBOOL (WINAPI *CreateUrl)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD);
    36     WINBOOL (WINAPI *GetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
    37     WINBOOL (WINAPI *GetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
    38     WINBOOL (WINAPI *GetProxyForUrl)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*);
    39     HINTERNET (WINAPI *Open)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD);
    40     HINTERNET (WINAPI *OpenRequest)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD);
    41     WINBOOL (WINAPI *QueryAuthParams)(HINTERNET,DWORD,LPVOID*);
    42     WINBOOL (WINAPI *QueryAuthSchemes)(HINTERNET,LPDWORD,LPDWORD,LPDWORD);
    43     WINBOOL (WINAPI *QueryDataAvailable)(HINTERNET,LPDWORD);
    44     WINBOOL (WINAPI *QueryHeaders)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD);
    45     WINBOOL (WINAPI *QueryOption)(HINTERNET,DWORD,LPVOID,LPDWORD);
    46     WINBOOL (WINAPI *ReadData)(HINTERNET,LPVOID,DWORD,LPDWORD);
    47     WINBOOL (WINAPI *ReceiveResponse)(HINTERNET,LPVOID);
    48     WINBOOL (WINAPI *SendRequest)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR);
    49     WINBOOL (WINAPI *SetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
    50     WINBOOL (WINAPI *SetCredentials)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID);
    51     WINBOOL (WINAPI *SetOption)(HINTERNET,DWORD,LPVOID,DWORD);
    52     WINHTTP_STATUS_CALLBACK (WINAPI *SetStatusCallback)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR);
    53     WINBOOL (WINAPI *SetTimeouts)(HINTERNET,int,int,int,int);
    54     WINBOOL (WINAPI *TimeFromSystemTime)(CONST SYSTEMTIME *,LPWSTR);
    55     WINBOOL (WINAPI *TimeToSystemTime)(LPCWSTR,SYSTEMTIME*);
    56     WINBOOL (WINAPI *WriteData)(HINTERNET,LPCVOID,DWORD,LPDWORD);
    57 } WinHttp;
    58 
    59 static int plover_init_winhttp(void)
    60 {
    61     HMODULE module;
    62     if (WinHttp.Open)
    63 	return 0;
    64     module=LoadLibraryA("winhttp.dll");
    65     if (!module)
    66 	return -1;
    67     WinHttp.AddRequestHeaders=
    68       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,DWORD))
    69       GetProcAddress(module,"WinHttpAddRequestHeaders");
    70     WinHttp.DetectAutoProxyConfigUrl=
    71       (WINBOOL (WINAPI *)(DWORD,LPWSTR*))
    72       GetProcAddress(module,"WinHttpDetectAutoProxyConfigUrl");
    73     WinHttp.CheckPlatform=(WINBOOL (WINAPI *)(void))
    74       GetProcAddress(module,"WinHttpCheckPlatform");
    75     WinHttp.CloseHandle=(WINBOOL (WINAPI *)(HINTERNET))
    76       GetProcAddress(module,"WinHttpCloseHandle");
    77     WinHttp.Connect=
    78       (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD))
    79       GetProcAddress(module,"WinHttpConnect");
    80     WinHttp.CrackUrl=(WINBOOL (WINAPI *)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS))
    81       GetProcAddress(module,"WinHttpCrackUrl");
    82     WinHttp.CreateUrl=
    83       (WINBOOL (WINAPI *)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD))
    84       GetProcAddress(module,"WinHttpCreateUrl");
    85     WinHttp.GetDefaultProxyConfiguration=
    86       (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*))
    87       GetProcAddress(module,"WinHttpGetDefaultProxyConfiguration");
    88     WinHttp.GetIEProxyConfigForCurrentUser=
    89       (WINBOOL (WINAPI *)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*))
    90       GetProcAddress(module,"WinHttpGetIEProxyConfigForCurrentUser");
    91     WinHttp.GetProxyForUrl=
    92       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*))
    93       GetProcAddress(module,"WinHttpGetProxyForUrl");
    94     WinHttp.Open=(HINTERNET (WINAPI *)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD))
    95       GetProcAddress(module,"WinHttpOpen");
    96     WinHttp.OpenRequest=
    97       (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD))
    98       GetProcAddress(module,"WinHttpOpenRequest");
    99     WinHttp.QueryAuthParams=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID*))
   100       GetProcAddress(module,"WinHttpQueryAuthParams");
   101     WinHttp.QueryAuthSchemes=
   102       (WINBOOL (WINAPI *)(HINTERNET,LPDWORD,LPDWORD,LPDWORD))
   103       GetProcAddress(module,"WinHttpQueryAuthSchemes");
   104     WinHttp.QueryDataAvailable=(WINBOOL (WINAPI *)(HINTERNET,LPDWORD))
   105       GetProcAddress(module,"WinHttpQueryDataAvailable");
   106     WinHttp.QueryHeaders=
   107       (WINBOOL (WINAPI *)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD))
   108       GetProcAddress(module,"WinHttpQueryHeaders");
   109     WinHttp.QueryOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,LPDWORD))
   110       GetProcAddress(module,"WinHttpQueryOption");
   111     WinHttp.ReadData=(WINBOOL (WINAPI *)(HINTERNET,LPVOID,DWORD,LPDWORD))
   112       GetProcAddress(module,"WinHttpReadData");
   113     WinHttp.ReceiveResponse=(WINBOOL (WINAPI *)(HINTERNET,LPVOID))
   114       GetProcAddress(module,"WinHttpReceiveResponse");
   115     WinHttp.SendRequest=
   116       (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR))
   117       GetProcAddress(module,"WinHttpSendRequest");
   118     WinHttp.SetDefaultProxyConfiguration=
   119       (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*))
   120       GetProcAddress(module,"WinHttpSetDefaultProxyConfiguration");
   121     WinHttp.SetCredentials=
   122       (WINBOOL (WINAPI *)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID))
   123       GetProcAddress(module,"WinHttpSetCredentials");
   124     WinHttp.SetOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,DWORD))
   125       GetProcAddress(module,"WinHttpSetOption");
   126     WinHttp.SetStatusCallback=
   127       (WINHTTP_STATUS_CALLBACK (WINAPI *)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR))
   128       GetProcAddress(module,"WinHttpSetStatusCallback");
   129     WinHttp.SetTimeouts=(WINBOOL (WINAPI *)(HINTERNET,int,int,int,int))
   130       GetProcAddress(module,"WinHttpSetTimeouts");
   131     WinHttp.TimeFromSystemTime=(WINBOOL (WINAPI *)(CONST SYSTEMTIME *,LPWSTR))
   132       GetProcAddress(module,"WinHttpTimeFromSystemTime");
   133     WinHttp.TimeToSystemTime=(WINBOOL (WINAPI *)(LPCWSTR,SYSTEMTIME*))
   134       GetProcAddress(module,"WinHttpTimeToSystemTime");
   135     WinHttp.WriteData=(WINBOOL (WINAPI *)(HINTERNET,LPCVOID,DWORD,LPDWORD))
   136       GetProcAddress(module,"WinHttpWriteData");
   137     if (!WinHttp.CrackUrl || !WinHttp.Open || !WinHttp.Connect ||
   138         !WinHttp.OpenRequest || !WinHttp.SendRequest ||
   139 	!WinHttp.ReceiveResponse || !WinHttp.QueryDataAvailable ||
   140 	!WinHttp.ReadData || !WinHttp.CloseHandle)
   141     {
   142 	FreeLibrary(module);
   143 	WinHttp.Open=NULL;
   144 	return -1;
   145     }
   146     return 0;
   147 }
   148 
   149 /*
   150  * This will find ${PREFIX}/${file} if it exists, but it may also
   151  * find ${PREFIX}/.../${file} if ${PREFIX}/${file} does not exist.
   152  * This seems unlikely and won't do any harm should it occur.
   153  */
   154 
   155 gchar *find_prefixed_file(const char *file)
   156 {
   157     int i,len;
   158     const char *name;
   159     char *install_root;
   160     struct razor_set *set;
   161     struct razor_error *error=NULL;
   162     struct razor_package *package;
   163     struct razor_package_iterator *pi;
   164     struct razor_file_iterator *fi;
   165     gchar *retval=NULL;
   166     len=strlen(file);
   167     install_root=getenv("RAZOR_ROOT");
   168     if (!install_root)
   169 	install_root="";
   170     set=razor_root_open_read_only(install_root,&error);
   171     if (set)
   172     {
   173 	pi=razor_package_iterator_create(set);
   174 	while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
   175 	{
   176 	    fi=razor_file_iterator_create(set,package,0);
   177 	    while (!retval && razor_file_iterator_next(fi,&name))
   178 	    {
   179 		i=strlen(name)-len;
   180 		if (i>0 && name[i-1]=='/' && !strcmp(name+i,file))
   181 		{
   182 		    if (!retval || strlen(retval)>strlen(name))
   183 		    {
   184 			g_free(retval);
   185 			retval=g_strdup(name);
   186 		    }
   187 		}
   188 	    }
   189 	    razor_file_iterator_destroy(fi);
   190 	}
   191 	razor_package_iterator_destroy(pi);
   192 	razor_set_unref(set);
   193     }
   194     if (error)
   195 	razor_error_free(error);
   196     return retval;
   197 }
   198 
   199 static HCERTSTORE plover_p12_import(const char *file,const wchar_t *password)
   200 {
   201     GError *error=NULL;
   202     gchar *p12,*path,*s;
   203     gsize len;
   204     CRYPT_DATA_BLOB pfx;
   205     HCERTSTORE store;
   206     s=g_build_path("/","etc","pki",file,NULL);
   207     path=find_prefixed_file(s);
   208     if (!path)
   209     {
   210 	g_printerr("%s: Not installed\n",s);
   211 	g_free(s);
   212 	return NULL;
   213     }
   214     g_free(s);
   215     if (!g_file_get_contents(path,&p12,&len,&error))
   216     {
   217 	g_printerr("%s: %s\n",path,error->message);
   218 	g_free(path);
   219 	g_error_free(error);
   220 	return NULL;
   221     }
   222     g_free(path);
   223     pfx.pbData=(BYTE *)p12;
   224     pfx.cbData=len;
   225     store=PFXImportCertStore(&pfx,password,PKCS12_NO_PERSIST_KEY);
   226     if (!store)
   227 	g_printerr("PFXImportCertStore failed. Err: %lu\n",GetLastError());
   228     g_free(p12);
   229     return store;
   230 }
   231 
   232 static const CERT_CONTEXT *plover_get_client_certificate(HCERTSTORE store)
   233 {
   234     const CERT_CONTEXT *iter,*cert;
   235     cert=NULL;
   236     iter=CertEnumCertificatesInStore(store,NULL);
   237     while(iter)
   238     {
   239 	if (!cert)
   240 	    cert=CertDuplicateCertificateContext(iter);
   241 	iter=CertEnumCertificatesInStore(store,iter);
   242     }
   243     return cert;
   244 }
   245 
   246 void CALLBACK plover_status_callback(HINTERNET handle,DWORD_PTR context,
   247   DWORD status,LPVOID status_information,DWORD status_information_length)
   248 {
   249     g_printerr("CB status 0x%lX",status);
   250     if (status==WINHTTP_CALLBACK_STATUS_SECURE_FAILURE)
   251 	g_printerr(", flag 0x%lX",*(DWORD *)status_information);
   252     g_printerr("\n");
   253 }
   254 
   255 int fetch(HINTERNET session,HCERTSTORE store,const char *url)
   256 {
   257     URL_COMPONENTS components={0};
   258     HINTERNET connection,request;
   259     DWORD len,nb,options;
   260     char *buffer;
   261     wchar_t *ws;
   262     glong ws_len;
   263     gchar *hostname,*path;
   264     const CERT_CONTEXT *client_cert;
   265     components.dwStructSize=sizeof(components);
   266     components.dwSchemeLength=(DWORD)-1;
   267     components.dwHostNameLength=(DWORD)-1;
   268     components.dwUserNameLength=(DWORD)-1;
   269     components.dwPasswordLength=(DWORD)-1;
   270     components.dwUrlPathLength=(DWORD)-1;
   271     ws=g_utf8_to_utf16(url,-1,NULL,&ws_len,NULL);
   272     if (!ws || !WinHttp.CrackUrl(ws,ws_len,0,&components))
   273     {
   274 	g_printerr("%s: Invalid or unsupported URL\n",url);
   275 	return -1;
   276     }
   277     /* ICU_REJECT_USERPWD is not supported under Windows XP */
   278     if (components.dwUserNameLength || components.dwPasswordLength)
   279     {
   280 	g_printerr("%s: Credentials not supported\n",url);
   281 	return -1;
   282     }
   283     hostname=g_utf16_to_utf8(components.lpszHostName,
   284       components.dwHostNameLength,NULL,NULL,NULL);
   285     path=g_utf16_to_utf8(components.lpszUrlPath,components.dwUrlPathLength,NULL,
   286       NULL,NULL);
   287     g_free(ws);
   288     ws=g_utf8_to_utf16(hostname,-1,NULL,NULL,NULL);
   289     connection=WinHttp.Connect(session,ws,
   290       components.nPort?components.nPort:INTERNET_DEFAULT_PORT,0);
   291     g_free(ws);
   292     if (connection)
   293     {
   294 	ws=g_utf8_to_utf16(path,-1,NULL,NULL,NULL);
   295 	request=WinHttp.OpenRequest(connection,L"GET",ws,NULL,
   296 	  WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES,
   297 	  components.nScheme==INTERNET_SCHEME_HTTPS?WINHTTP_FLAG_SECURE:0);
   298 	g_free(ws);
   299 	if (request)
   300 	{
   301 	    if (debug)
   302 		(void)WinHttp.SetStatusCallback(request,plover_status_callback,
   303 		  WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,0);
   304 	    if (components.nScheme==INTERNET_SCHEME_HTTPS && WinHttp.SetOption)
   305 	    {
   306 		options=SECURITY_FLAG_IGNORE_UNKNOWN_CA;
   307 		(void)WinHttp.SetOption(request,WINHTTP_OPTION_SECURITY_FLAGS,
   308 		  &options,sizeof(options));
   309 		client_cert=plover_get_client_certificate(store);
   310 		if (client_cert)
   311 		{
   312 		    if (!WinHttp.SetOption(request,
   313 		      WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(void *)client_cert,
   314 		      sizeof(*client_cert)))
   315 			g_printerr("Failed to set client certificate (%lu)\n",
   316 			  GetLastError());
   317 		}
   318 		else
   319 		    g_printerr("No client certificate found\n");
   320 	    }
   321 	    if (!WinHttp.SendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS,
   322 	      0,WINHTTP_NO_REQUEST_DATA,0,0,0))
   323 		g_printerr("Failed to send request (%lu)\n",GetLastError());
   324 	    else if (!WinHttp.ReceiveResponse(request,NULL))
   325 		g_printerr("Failed to receive response (%lu)\n",GetLastError());
   326 	    else
   327 	    {
   328 		do
   329 		{
   330 		    len=0;
   331 		    if (!WinHttp.QueryDataAvailable(request,&len))
   332 			g_printerr(
   333 			  "Error %lu in WinHttpQueryDataAvailable.\n",
   334 			  (unsigned long)GetLastError());
   335 		    buffer=calloc(len+1,1);
   336 		    if (!buffer)
   337 			break;
   338 		    else
   339 		    {
   340 			if (!WinHttp.ReadData(request,(void *)buffer,len,&nb))
   341 			    g_printerr("Error %lu in WinHttpReadData.\n",
   342 			      (unsigned long)GetLastError());
   343 			else
   344 			    printf("%s\n",buffer);
   345 			free(buffer);
   346 		    }
   347 		} while (len>0);
   348 	    }
   349 	    WinHttp.CloseHandle(request);
   350 	}
   351 	else
   352 	    g_printerr("Failed to open request for %s (%lu)\n",path,
   353 	      GetLastError());
   354 	WinHttp.CloseHandle(connection);
   355     }
   356     else
   357 	g_printerr("Failed to open connection to %s\n",hostname);
   358     g_free(hostname);
   359     g_free(path);
   360     return 0;
   361 }
   362 
   363 int main(int argc,char **argv)
   364 {
   365     GError *error=NULL;
   366     GOptionContext *context;
   367     HCERTSTORE store;
   368     HINTERNET session;
   369     wchar_t *ws;
   370     context=g_option_context_new("URL - fetch a URL");
   371     g_option_context_add_main_entries(context,entries,NULL);
   372     if (!g_option_context_parse(context,&argc,&argv,&error))
   373     {
   374 	g_printerr("Option parsing failed: %s\n",error->message);
   375 	exit(1);
   376     }
   377     if (argc!=2)
   378     {
   379 	g_printerr("%s\n",g_option_context_get_help(context,TRUE,NULL));
   380 	exit(1);
   381     }
   382     if (plover_init_winhttp())
   383     {
   384 	g_printerr("HTTP is not supported on this machine\n");
   385 	exit(1);
   386     }
   387     store=plover_p12_import("system.p12",L"xyzzy-ylem");
   388     if (!store)
   389 	exit(1);
   390     ws=g_utf8_to_utf16(PACKAGE_NAME "/" PACKAGE_VERSION,-1,NULL,NULL,NULL);
   391     session=WinHttp.Open(ws,WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
   392       WINHTTP_NO_PROXY_NAME,WINHTTP_NO_PROXY_BYPASS,0);
   393     free(ws);
   394     if (!session)
   395     {
   396 	g_printerr("Failed to open WinHttp session\n");
   397 	exit(1);
   398     }
   399     if (fetch(session,store,argv[1]))
   400 	exit(1);
   401     WinHttp.CloseHandle(session);
   402     CertCloseStore(store,CERT_CLOSE_STORE_FORCE_FLAG);
   403     exit(0);
   404 }
   405 #else	/* !HAVE_WINHTTP_H */
   406 main()
   407 {
   408     g_printerr("HTTP is not supported on this machine\n");
   409     exit(1);
   410 }
   411 #endif	/* HAVE_WINHTTP_H */