diff -r 000000000000 -r 5cafc65a6183 app-manager/fetch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app-manager/fetch.c Tue Feb 21 22:55:40 2012 +0000 @@ -0,0 +1,411 @@ +/* + * A program to explore http support on MS-Windows. + */ + +#include "config.h" +#include +#include +#include +#include +#if HAVE_WINHTTP_H +#include +#include +#include + +#ifndef PKCS12_NO_PERSIST_KEY +#define PKCS12_NO_PERSIST_KEY 0x00008000 +#endif + +static gboolean debug=FALSE; + +static GOptionEntry entries[] = +{ + { "debug",'d',0,G_OPTION_ARG_NONE,&debug,"Output debugging",NULL }, + { NULL } +}; + +static struct WinHttpFuncs +{ + WINBOOL (WINAPI *AddRequestHeaders)(HINTERNET,LPCWSTR,DWORD,DWORD); + WINBOOL (WINAPI *DetectAutoProxyConfigUrl)(DWORD,LPWSTR*); + WINBOOL (WINAPI *CheckPlatform)(void); + WINBOOL (WINAPI *CloseHandle)(HINTERNET); + HINTERNET (WINAPI *Connect)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD); + WINBOOL (WINAPI *CrackUrl)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS); + WINBOOL (WINAPI *CreateUrl)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD); + WINBOOL (WINAPI *GetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*); + WINBOOL (WINAPI *GetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*); + WINBOOL (WINAPI *GetProxyForUrl)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*); + HINTERNET (WINAPI *Open)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD); + HINTERNET (WINAPI *OpenRequest)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD); + WINBOOL (WINAPI *QueryAuthParams)(HINTERNET,DWORD,LPVOID*); + WINBOOL (WINAPI *QueryAuthSchemes)(HINTERNET,LPDWORD,LPDWORD,LPDWORD); + WINBOOL (WINAPI *QueryDataAvailable)(HINTERNET,LPDWORD); + WINBOOL (WINAPI *QueryHeaders)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD); + WINBOOL (WINAPI *QueryOption)(HINTERNET,DWORD,LPVOID,LPDWORD); + WINBOOL (WINAPI *ReadData)(HINTERNET,LPVOID,DWORD,LPDWORD); + WINBOOL (WINAPI *ReceiveResponse)(HINTERNET,LPVOID); + WINBOOL (WINAPI *SendRequest)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR); + WINBOOL (WINAPI *SetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*); + WINBOOL (WINAPI *SetCredentials)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID); + WINBOOL (WINAPI *SetOption)(HINTERNET,DWORD,LPVOID,DWORD); + WINHTTP_STATUS_CALLBACK (WINAPI *SetStatusCallback)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR); + WINBOOL (WINAPI *SetTimeouts)(HINTERNET,int,int,int,int); + WINBOOL (WINAPI *TimeFromSystemTime)(CONST SYSTEMTIME *,LPWSTR); + WINBOOL (WINAPI *TimeToSystemTime)(LPCWSTR,SYSTEMTIME*); + WINBOOL (WINAPI *WriteData)(HINTERNET,LPCVOID,DWORD,LPDWORD); +} WinHttp; + +static int plover_init_winhttp(void) +{ + HMODULE module; + if (WinHttp.Open) + return 0; + module=LoadLibraryA("winhttp.dll"); + if (!module) + return -1; + WinHttp.AddRequestHeaders= + (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,DWORD)) + GetProcAddress(module,"WinHttpAddRequestHeaders"); + WinHttp.DetectAutoProxyConfigUrl= + (WINBOOL (WINAPI *)(DWORD,LPWSTR*)) + GetProcAddress(module,"WinHttpDetectAutoProxyConfigUrl"); + WinHttp.CheckPlatform=(WINBOOL (WINAPI *)(void)) + GetProcAddress(module,"WinHttpCheckPlatform"); + WinHttp.CloseHandle=(WINBOOL (WINAPI *)(HINTERNET)) + GetProcAddress(module,"WinHttpCloseHandle"); + WinHttp.Connect= + (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD)) + GetProcAddress(module,"WinHttpConnect"); + WinHttp.CrackUrl=(WINBOOL (WINAPI *)(LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS)) + GetProcAddress(module,"WinHttpCrackUrl"); + WinHttp.CreateUrl= + (WINBOOL (WINAPI *)(LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD)) + GetProcAddress(module,"WinHttpCreateUrl"); + WinHttp.GetDefaultProxyConfiguration= + (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*)) + GetProcAddress(module,"WinHttpGetDefaultProxyConfiguration"); + WinHttp.GetIEProxyConfigForCurrentUser= + (WINBOOL (WINAPI *)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*)) + GetProcAddress(module,"WinHttpGetIEProxyConfigForCurrentUser"); + WinHttp.GetProxyForUrl= + (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,WINHTTP_AUTOPROXY_OPTIONS*,WINHTTP_PROXY_INFO*)) + GetProcAddress(module,"WinHttpGetProxyForUrl"); + WinHttp.Open=(HINTERNET (WINAPI *)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD)) + GetProcAddress(module,"WinHttpOpen"); + WinHttp.OpenRequest= + (HINTERNET (WINAPI *)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD)) + GetProcAddress(module,"WinHttpOpenRequest"); + WinHttp.QueryAuthParams=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID*)) + GetProcAddress(module,"WinHttpQueryAuthParams"); + WinHttp.QueryAuthSchemes= + (WINBOOL (WINAPI *)(HINTERNET,LPDWORD,LPDWORD,LPDWORD)) + GetProcAddress(module,"WinHttpQueryAuthSchemes"); + WinHttp.QueryDataAvailable=(WINBOOL (WINAPI *)(HINTERNET,LPDWORD)) + GetProcAddress(module,"WinHttpQueryDataAvailable"); + WinHttp.QueryHeaders= + (WINBOOL (WINAPI *)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD)) + GetProcAddress(module,"WinHttpQueryHeaders"); + WinHttp.QueryOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,LPDWORD)) + GetProcAddress(module,"WinHttpQueryOption"); + WinHttp.ReadData=(WINBOOL (WINAPI *)(HINTERNET,LPVOID,DWORD,LPDWORD)) + GetProcAddress(module,"WinHttpReadData"); + WinHttp.ReceiveResponse=(WINBOOL (WINAPI *)(HINTERNET,LPVOID)) + GetProcAddress(module,"WinHttpReceiveResponse"); + WinHttp.SendRequest= + (WINBOOL (WINAPI *)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR)) + GetProcAddress(module,"WinHttpSendRequest"); + WinHttp.SetDefaultProxyConfiguration= + (WINBOOL (WINAPI *)(WINHTTP_PROXY_INFO*)) + GetProcAddress(module,"WinHttpSetDefaultProxyConfiguration"); + WinHttp.SetCredentials= + (WINBOOL (WINAPI *)(HINTERNET,DWORD,DWORD,LPCWSTR,LPCWSTR,LPVOID)) + GetProcAddress(module,"WinHttpSetCredentials"); + WinHttp.SetOption=(WINBOOL (WINAPI *)(HINTERNET,DWORD,LPVOID,DWORD)) + GetProcAddress(module,"WinHttpSetOption"); + WinHttp.SetStatusCallback= + (WINHTTP_STATUS_CALLBACK (WINAPI *)(HINTERNET,WINHTTP_STATUS_CALLBACK,DWORD,DWORD_PTR)) + GetProcAddress(module,"WinHttpSetStatusCallback"); + WinHttp.SetTimeouts=(WINBOOL (WINAPI *)(HINTERNET,int,int,int,int)) + GetProcAddress(module,"WinHttpSetTimeouts"); + WinHttp.TimeFromSystemTime=(WINBOOL (WINAPI *)(CONST SYSTEMTIME *,LPWSTR)) + GetProcAddress(module,"WinHttpTimeFromSystemTime"); + WinHttp.TimeToSystemTime=(WINBOOL (WINAPI *)(LPCWSTR,SYSTEMTIME*)) + GetProcAddress(module,"WinHttpTimeToSystemTime"); + WinHttp.WriteData=(WINBOOL (WINAPI *)(HINTERNET,LPCVOID,DWORD,LPDWORD)) + GetProcAddress(module,"WinHttpWriteData"); + if (!WinHttp.CrackUrl || !WinHttp.Open || !WinHttp.Connect || + !WinHttp.OpenRequest || !WinHttp.SendRequest || + !WinHttp.ReceiveResponse || !WinHttp.QueryDataAvailable || + !WinHttp.ReadData || !WinHttp.CloseHandle) + { + FreeLibrary(module); + WinHttp.Open=NULL; + return -1; + } + return 0; +} + +/* + * This will find ${PREFIX}/${file} if it exists, but it may also + * find ${PREFIX}/.../${file} if ${PREFIX}/${file} does not exist. + * This seems unlikely and won't do any harm should it occur. + */ + +gchar *find_prefixed_file(const char *file) +{ + int i,len; + const char *name; + char *install_root; + struct razor_set *set; + struct razor_atomic *atomic; + struct razor_package *package; + struct razor_package_iterator *pi; + struct razor_file_iterator *fi; + gchar *retval=NULL; + len=strlen(file); + install_root=getenv("RAZOR_ROOT"); + if (!install_root) + install_root=""; + atomic=razor_atomic_open("Query packages"); + set=razor_root_open_read_only(install_root,atomic); + if (set) + { + pi=razor_package_iterator_create(set); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) + { + fi=razor_file_iterator_create(set,package,0); + while (!retval && razor_file_iterator_next(fi,&name)) + { + i=strlen(name)-len; + if (i>0 && name[i-1]=='/' && !strcmp(name+i,file)) + { + if (!retval || strlen(retval)>strlen(name)) + { + g_free(retval); + retval=g_strdup(name); + } + } + } + razor_file_iterator_destroy(fi); + } + razor_package_iterator_destroy(pi); + razor_set_unref(set); + } + razor_atomic_destroy(atomic); + return retval; +} + +static HCERTSTORE plover_p12_import(const char *file,const wchar_t *password) +{ + GError *error=NULL; + gchar *p12,*path,*s; + gsize len; + CRYPT_DATA_BLOB pfx; + HCERTSTORE store; + s=g_build_path("/","etc","pki",file,NULL); + path=find_prefixed_file(s); + if (!path) + { + g_printerr("%s: Not installed\n",s); + g_free(s); + return NULL; + } + g_free(s); + if (!g_file_get_contents(path,&p12,&len,&error)) + { + g_printerr("%s: %s\n",path,error->message); + g_free(path); + g_error_free(error); + return NULL; + } + g_free(path); + pfx.pbData=(BYTE *)p12; + pfx.cbData=len; + store=PFXImportCertStore(&pfx,password,PKCS12_NO_PERSIST_KEY); + if (!store) + g_printerr("PFXImportCertStore failed. Err: %lu\n",GetLastError()); + g_free(p12); + return store; +} + +static const CERT_CONTEXT *plover_get_client_certificate(HCERTSTORE store) +{ + const CERT_CONTEXT *iter,*cert; + cert=NULL; + iter=CertEnumCertificatesInStore(store,NULL); + while(iter) + { + if (!cert) + cert=CertDuplicateCertificateContext(iter); + iter=CertEnumCertificatesInStore(store,iter); + } + return cert; +} + +void CALLBACK plover_status_callback(HINTERNET handle,DWORD_PTR context, + DWORD status,LPVOID status_information,DWORD status_information_length) +{ + g_printerr("CB status 0x%lX",status); + if (status==WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) + g_printerr(", flag 0x%lX",*(DWORD *)status_information); + g_printerr("\n"); +} + +int fetch(HINTERNET session,HCERTSTORE store,const char *url) +{ + URL_COMPONENTS components={0}; + HINTERNET connection,request; + DWORD len,nb,options; + char *buffer; + wchar_t *ws; + glong ws_len; + gchar *hostname,*path; + const CERT_CONTEXT *client_cert; + components.dwStructSize=sizeof(components); + components.dwSchemeLength=(DWORD)-1; + components.dwHostNameLength=(DWORD)-1; + components.dwUserNameLength=(DWORD)-1; + components.dwPasswordLength=(DWORD)-1; + components.dwUrlPathLength=(DWORD)-1; + ws=g_utf8_to_utf16(url,-1,NULL,&ws_len,NULL); + if (!ws || !WinHttp.CrackUrl(ws,ws_len,0,&components)) + { + g_printerr("%s: Invalid or unsupported URL\n",url); + return -1; + } + /* ICU_REJECT_USERPWD is not supported under Windows XP */ + if (components.dwUserNameLength || components.dwPasswordLength) + { + g_printerr("%s: Credentials not supported\n",url); + return -1; + } + hostname=g_utf16_to_utf8(components.lpszHostName, + components.dwHostNameLength,NULL,NULL,NULL); + path=g_utf16_to_utf8(components.lpszUrlPath,components.dwUrlPathLength,NULL, + NULL,NULL); + g_free(ws); + ws=g_utf8_to_utf16(hostname,-1,NULL,NULL,NULL); + connection=WinHttp.Connect(session,ws, + components.nPort?components.nPort:INTERNET_DEFAULT_PORT,0); + g_free(ws); + if (connection) + { + ws=g_utf8_to_utf16(path,-1,NULL,NULL,NULL); + request=WinHttp.OpenRequest(connection,L"GET",ws,NULL, + WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES, + components.nScheme==INTERNET_SCHEME_HTTPS?WINHTTP_FLAG_SECURE:0); + g_free(ws); + if (request) + { + if (debug) + (void)WinHttp.SetStatusCallback(request,plover_status_callback, + WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,0); + if (components.nScheme==INTERNET_SCHEME_HTTPS && WinHttp.SetOption) + { + options=SECURITY_FLAG_IGNORE_UNKNOWN_CA; + (void)WinHttp.SetOption(request,WINHTTP_OPTION_SECURITY_FLAGS, + &options,sizeof(options)); + client_cert=plover_get_client_certificate(store); + if (client_cert) + { + if (!WinHttp.SetOption(request, + WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(void *)client_cert, + sizeof(*client_cert))) + g_printerr("Failed to set client certificate (%lu)\n", + GetLastError()); + } + else + g_printerr("No client certificate found\n"); + } + if (!WinHttp.SendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS, + 0,WINHTTP_NO_REQUEST_DATA,0,0,0)) + g_printerr("Failed to send request (%lu)\n",GetLastError()); + else if (!WinHttp.ReceiveResponse(request,NULL)) + g_printerr("Failed to receive response (%lu)\n",GetLastError()); + else + { + do + { + len=0; + if (!WinHttp.QueryDataAvailable(request,&len)) + g_printerr( + "Error %lu in WinHttpQueryDataAvailable.\n", + (unsigned long)GetLastError()); + buffer=calloc(len+1,1); + if (!buffer) + break; + else + { + if (!WinHttp.ReadData(request,(void *)buffer,len,&nb)) + g_printerr("Error %lu in WinHttpReadData.\n", + (unsigned long)GetLastError()); + else + printf("%s\n",buffer); + free(buffer); + } + } while (len>0); + } + WinHttp.CloseHandle(request); + } + else + g_printerr("Failed to open request for %s (%lu)\n",path, + GetLastError()); + WinHttp.CloseHandle(connection); + } + else + g_printerr("Failed to open connection to %s\n",hostname); + g_free(hostname); + g_free(path); + return 0; +} + +int main(int argc,char **argv) +{ + GError *error=NULL; + GOptionContext *context; + HCERTSTORE store; + HINTERNET session; + wchar_t *ws; + context=g_option_context_new("URL - fetch a URL"); + g_option_context_add_main_entries(context,entries,NULL); + if (!g_option_context_parse(context,&argc,&argv,&error)) + { + g_printerr("Option parsing failed: %s\n",error->message); + exit(1); + } + if (argc!=2) + { + g_printerr("%s\n",g_option_context_get_help(context,TRUE,NULL)); + exit(1); + } + if (plover_init_winhttp()) + { + g_printerr("HTTP is not supported on this machine\n"); + exit(1); + } + store=plover_p12_import("system.p12",L"xyzzy-ylem"); + if (!store) + exit(1); + ws=g_utf8_to_utf16(PACKAGE_NAME "/" PACKAGE_VERSION,-1,NULL,NULL,NULL); + session=WinHttp.Open(ws,WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME,WINHTTP_NO_PROXY_BYPASS,0); + free(ws); + if (!session) + { + g_printerr("Failed to open WinHttp session\n"); + exit(1); + } + if (fetch(session,store,argv[1])) + exit(1); + WinHttp.CloseHandle(session); + CertCloseStore(store,CERT_CLOSE_STORE_FORCE_FLAG); + exit(0); +} +#else /* !HAVE_WINHTTP_H */ +main() +{ + g_printerr("HTTP is not supported on this machine\n"); + exit(1); +} +#endif /* HAVE_WINHTTP_H */