FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

#include #include #include #include #include #include "helpers.h" #include "proxy.h" #ifdef BITS_INJECT_ERROR static HRESULT _inject_hr[] #else const HRESULT _inject_hr[] #endif = { S_OK, S_OK, S_OK, S_OK }; static PyObject * error_from_bits_hr(IBackgroundCopyManager *bcm, HRESULT hr, const char *operation) { LPWSTR err; HRESULT hr2; if (FAILED(hr2 = _inject_hr[2]) || FAILED(hr2 = bcm->GetErrorDescription(hr, LANGIDFROMLCID(GetThreadLocale()), &err))) { err_SetFromWindowsErrWithMessage(hr, operation); err_SetFromWindowsErrWithMessage(hr2, "Retrieving error message"); return NULL; } size_t n = wcslen(err); while (n > 0 && isspace(err[--n])) { err[n] = L'\0'; } err_SetFromWindowsErrWithMessage(hr, operation, err); CoTaskMemFree(err); return NULL; } static PyObject * error_from_bits_job(IBackgroundCopyJob *job) { IBackgroundCopyError *error = NULL; BG_ERROR_CONTEXT context; HRESULT hr, hr_error = S_FALSE; LPWSTR str_error; PyObject *exc, *val, *tb; if (FAILED(hr = _inject_hr[1]) || FAILED(hr = job->GetError(&error)) || FAILED(hr = error->GetError(&context, &hr_error)) ) { if (error) { error->Release(); } PyErr_SetString(PyExc_OSError, "Unidentified download error"); err_SetFromWindowsErrWithMessage(hr, "Retrieving download error"); return NULL; } if (FAILED(hr = _inject_hr[2]) || FAILED(hr = error->GetErrorDescription(LANGIDFROMLCID(GetThreadLocale()), &str_error)) ) { error->Release(); //PyErr_SetFromWindowsErr(hr_error); //err_SetFromWindowsErrWithMessage(hr, "Retrieving error message"); err_SetFromWindowsErrWithMessage(hr_error, "Could not retrieve message"); return NULL; } size_t n = wcslen(str_error); while (n > 0 && isspace(str_error[--n])) { str_error[n] = L'\0'; } err_SetFromWindowsErrWithMessage(hr_error, "Download error", str_error); CoTaskMemFree(str_error); error->Release(); return NULL; } static HRESULT get_job_progress(IBackgroundCopyJob *job, int *progress, int *already_complete) { HRESULT hr; BG_JOB_STATE job_state; BG_JOB_PROGRESS job_progress; if (FAILED(hr = _inject_hr[0]) || FAILED(hr = job->GetState(&job_state))) { return hr; } *already_complete = 0; switch (job_state) { case BG_JOB_STATE_QUEUED: case BG_JOB_STATE_CONNECTING: case BG_JOB_STATE_CANCELLED: *progress = 0; break; case BG_JOB_STATE_TRANSFERRED: *progress = 100; break; case BG_JOB_STATE_ACKNOWLEDGED: *progress = 100; *already_complete = 1; break; case BG_JOB_STATE_TRANSFERRING: case BG_JOB_STATE_SUSPENDED: if (FAILED(hr = job->GetProgress(&job_progress))) { return hr; } // probably an unnecessary sanity check if (job_progress.FilesTransferred >= job_progress.FilesTotal || job_progress.BytesTransferred >= job_progress.BytesTotal) { *progress = 100; } else if (job_progress.BytesTotal == BG_SIZE_UNKNOWN) { *progress = (job_progress.FilesTransferred * 100) / job_progress.FilesTotal; } else { *progress = (job_progress.BytesTransferred * 100) / job_progress.BytesTotal; } break; case BG_JOB_STATE_TRANSIENT_ERROR: case BG_JOB_STATE_ERROR: return S_FALSE; } if (*progress < 0) { *progress = 0; } else if (*progress > 100) { *progress = 100; } return S_OK; } extern "C" { // Returns a capsule containing the BackgroundCopyManager instance PyObject *bits_connect(PyObject *, PyObject *args, PyObject *kwargs) { IBackgroundCopyManager *bcm = NULL; HRESULT hr = CoCreateInstance( __uuidof(BackgroundCopyManager), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyManager), (void**)&bcm ); if (FAILED(hr)) { PyErr_SetFromWindowsErr(hr); return NULL; } return make_capsule(bcm); } // (conn, job_id) -> job PyObject *bits_find_job(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"conn", "job_id", NULL}; IBackgroundCopyManager *bcm = NULL; Py_buffer job_id; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&y*:bits_find_job", keywords, from_capsule, &bcm, &job_id )) { return NULL; } if (job_id.len < sizeof(GUID)) { PyBuffer_Release(&job_id); PyErr_SetString(PyExc_ValueError, "'job_id' must be a serialized job ID"); return NULL; } PyObject *r = NULL; IBackgroundCopyJob *job = NULL; GUID job_guid = *(GUID *)job_id.buf; HRESULT hr; if (FAILED(hr = _inject_hr[0]) || FAILED(hr = bcm->GetJob(job_guid, &job))) { error_from_bits_hr(bcm, hr, "Getting background download"); } else { r = make_capsule(job); } PyBuffer_Release(&job_id); return r; } // (conn, job) -> job_id PyObject *bits_serialize_job(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"conn", "job", NULL}; IBackgroundCopyManager *bcm = NULL; IBackgroundCopyJob* job = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&:bits_serialize_job", keywords, from_capsule, &bcm, from_capsule, &job)) { return NULL; } PyObject *r = NULL; GUID job_id; HRESULT hr; if (FAILED(hr = _inject_hr[0]) || FAILED(hr = job->GetId(&job_id))) { error_from_bits_hr(bcm, hr, "Getting download job ID"); } else { r = PyBytes_FromStringAndSize((char *)&job_id, sizeof(job_id)); } return r; } static HRESULT _job_setproxy(IBackgroundCopyJob *job, const ProxySettings *settings) { if (settings->mode == PROXY_MODE_DIRECT) { return job->SetProxySettings(BG_JOB_PROXY_USAGE_NO_PROXY, NULL, NULL); } if (settings->mode == PROXY_MODE_OVERRIDE) { return job->SetProxySettings( BG_JOB_PROXY_USAGE_OVERRIDE, settings->proxy_list, NULL ); } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config = { 0 }; if (WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) { if (proxy_config.lpszProxy || proxy_config.lpszAutoConfigUrl || proxy_config.fAutoDetect) { // Global settings are present, and so BITS's default behaviour // should be fine. if (proxy_config.lpszProxy) { GlobalFree(proxy_config.lpszProxy); } if (proxy_config.lpszProxyBypass) { GlobalFree(proxy_config.lpszProxyBypass); } if (proxy_config.lpszAutoConfigUrl) { GlobalFree(proxy_config.lpszAutoConfigUrl); } return S_OK; } } // Probably no static settings, so tell BITS to do autodetection. return job->SetProxySettings(BG_JOB_PROXY_USAGE_AUTODETECT, NULL, NULL); } static HRESULT _job_setcredentials( IBackgroundCopyJob *job, BG_AUTH_TARGET target, wchar_t *username, wchar_t *password ) { IBackgroundCopyJob2 *job2 = NULL; HRESULT hr; BG_AUTH_CREDENTIALS creds = { .Target = target, .Scheme = username ? BG_AUTH_SCHEME_BASIC : BG_AUTH_SCHEME_NEGOTIATE, .Credentials = { .Basic = { .UserName = username, .Password = username ? password : NULL } } }; if (FAILED(hr = _inject_hr[3]) || FAILED(hr = job->QueryInterface(__uuidof(IBackgroundCopyJob2), (void **)&job2))) { return hr; } hr = job2->SetCredentials(&creds); job2->Release(); return hr; } // (conn, name, url, path, [username], [password], [proxy_settings]) -> job PyObject *bits_begin(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = { "conn", "name", "url", "path", "username", "password", "proxy_settings", NULL }; IBackgroundCopyManager *bcm = NULL; wchar_t *name = NULL; wchar_t *url = NULL; wchar_t *path = NULL; wchar_t *username = NULL; wchar_t *password = NULL; PyObject *proxy_settings_obj = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&O&|O&O&O:bits_begin", keywords, from_capsule, &bcm, as_utf16, &name, as_utf16, &url, as_utf16, &path, as_utf16, &username, as_utf16, &password, &proxy_settings_obj )) { return NULL; } PyObject *r = NULL; GUID jobId; IBackgroundCopyJob* job = NULL; ProxySettings proxy_settings = {}; HRESULT hr; if (!proxy_settings_parse(proxy_settings_obj, &proxy_settings)) { goto done; } if (FAILED(hr = _inject_hr[0]) || FAILED(hr = bcm->CreateJob(name, BG_JOB_TYPE_DOWNLOAD, &jobId, &job))) { error_from_bits_hr(bcm, hr, "Creating download job"); goto done; } if (FAILED(hr = _job_setproxy(job, &proxy_settings))) { error_from_bits_hr(bcm, hr, "Setting proxy"); goto done; } // Explicit proxy credentials come from the configured proxy URL. NULL // automatically infers credentials if needed, which remains the default. if (FAILED(hr = _job_setcredentials( job, BG_AUTH_TARGET_PROXY, proxy_settings.username, proxy_settings.password ))) { error_from_bits_hr(bcm, hr, "Setting proxy credentials"); goto done; } if (FAILED(hr = _job_setcredentials(job, BG_AUTH_TARGET_SERVER, username, password))) { error_from_bits_hr(bcm, hr, "Adding basic credentials to download job"); goto done; } if (FAILED(hr = job->AddFile(url, path))) { error_from_bits_hr(bcm, hr, "Adding file to download job"); goto done; } if (FAILED(hr = job->SetPriority(BG_JOB_PRIORITY_FOREGROUND))) { error_from_bits_hr(bcm, hr, "Setting download job priority"); goto done; } if (FAILED(hr = job->Resume())) { error_from_bits_hr(bcm, hr, "Starting download job"); goto done; } job->AddRef(); r = make_capsule(job); done: if (job) { job->Release(); } proxy_settings_clear(&proxy_settings); PyMem_Free(path); PyMem_Free(url); PyMem_Free(name); PyMem_Free(username); PyMem_Free(password); return r; } // (job) PyObject *bits_cancel(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"conn", "job", NULL}; IBackgroundCopyManager *bcm = NULL; IBackgroundCopyJob* job = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&:bits_cancel", keywords, from_capsule, &bcm, from_capsule, &job)) { return NULL; } HRESULT hr; if (FAILED(hr = _inject_hr[0]) || FAILED(hr = job->Cancel())) { return error_from_bits_hr(bcm, hr, "Cancelling download job"); } Py_RETURN_NONE; } // (job) -> int[0..100] or exception PyObject *bits_get_progress(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"conn", "job", NULL}; IBackgroundCopyManager *bcm = NULL; IBackgroundCopyJob* job = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&:bits_get_progress", keywords, from_capsule, &bcm, from_capsule, &job)) { return NULL; } PyObject *r = NULL; int progress = 0, already_complete = 0; HRESULT hr = _inject_hr[0]; if (!FAILED(hr) && hr != S_FALSE) { hr = get_job_progress(job, &progress, &already_complete); } if (hr == S_FALSE) { return error_from_bits_job(job); } else if (FAILED(hr)) { return error_from_bits_hr(bcm, hr, "Getting download progress"); } if (progress == 100 && !already_complete) { hr = job->Complete(); if (FAILED(hr)) { return error_from_bits_hr(bcm, hr, "Completing download job"); } } return Py_BuildValue("i", progress); } // (conn, job, username, password) -> job PyObject *bits_retry_with_auth(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"conn", "job", "username", "password", NULL}; IBackgroundCopyManager *bcm = NULL; IBackgroundCopyJob* job = NULL; wchar_t *username = NULL; wchar_t *password = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&O&O&:bits_retry_with_auth", keywords, from_capsule, &bcm, from_capsule, &job, as_utf16, &username, as_utf16, &password )) { return NULL; } HRESULT hr; PyObject *r = NULL; if (FAILED(hr = _job_setcredentials(job, BG_AUTH_TARGET_SERVER, username, password))) { error_from_bits_hr(bcm, hr, "Adding basic credentials to download job"); goto done; } if (FAILED(hr = job->Resume())) { error_from_bits_hr(bcm, hr, "Starting download job"); goto done; } r = Py_GetConstant(Py_CONSTANT_NONE); done: PyMem_Free(username); PyMem_Free(password); return r; } #ifdef BITS_INJECT_ERROR PyObject *bits_inject_error(PyObject *, PyObject *args, PyObject *kwargs) { HRESULT hr; if (!PyArg_ParseTuple( args, "IIII:bits_inject_error", // replace HRESULT for primary operation &_inject_hr[0], // replace HRESULT for getting error code &_inject_hr[1], // replace HRESULT for getting error text &_inject_hr[2], // replace HRESULT for adding credentials to job &_inject_hr[3] )) { return NULL; } Py_RETURN_NONE; } #endif }

Back | FazBrowse Home | New Git URL