root/lang/c/AutoUpdater/trunk/recipes/Template.c @ 38024

Revision 38024, 40.3 kB (checked in by saturday06, 4 years ago)

goto error return

Line 
1#define AUI_NO_UNDEFINE_MACROS
2#include "AutoUpdater.h"
3
4#include <winuser.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <time.h>
10#include <wchar.h>
11#include <wininet.h>
12#include <shlwapi.h>
13
14#ifndef __CYGWIN__
15#include <tchar.h>
16#endif
17
18#ifdef _MSC_VER
19#pragma comment(lib, "wininet.lib")
20#pragma comment(lib, "shlwapi.lib")
21#if defined(_CRT_NONSTDC_DEPRECATE) && !defined(_CRT_NONSTDC_NO_WARNINGS) && !defined(_POSIX_)
22/* #define wcsdup _wcsdup */
23#endif
24#endif
25
26
27#undef strdup
28#undef _strdup
29#undef wcsdup
30#undef _wcsdup
31
32#define strdup AUI_strdup
33#define wcsdup AUI_wcsdup
34#define _strdup AUI_strdup
35#define _wcsdup AUI_wcsdup
36
37
38#ifdef __CYGWIN__
39
40#  define _T(x) TEXT(x)
41#  define GUID_NULL AUI_GUID_NULL
42#  define IID_IDispatch AUI_IID_IDispatch
43
44const GUID AUI_GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0 }};
45const GUID AUI_IID_IDispatch = {0x00020400, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
46
47#  ifdef UNICODE
48#    define _tcslen wcslen
49#    define _tcscpy wcscpy
50#    define _tcscat wcscat
51#    define _tcsdup AUI_wcsdup
52#    define _tfopen(x, y) (NULL)
53#  else
54#    define _tcslen strlen
55#    define _tcscpy strcpy
56#    define _tcscat strcat
57#    define _tcsdup AUI_strdup
58#    define _tfopen fopen
59#  endif
60
61#endif
62
63#if !defined(__STDC_WANT_SECURE_LIB__) || ! __STDC_WANT_SECURE_LIB__
64#define _tfopen_s(x, y, z) {*x = _tfopen(y, z);}
65#define _tcscpy_s(x, y, z) _tcscpy(x, z)
66#define _tcscat_s(x, y, z) _tcscat(x, z)
67#endif
68
69char* AUI_strdup(const char* s) {
70    size_t bytes = 0;
71    char* buf = NULL;
72    bytes = strlen(s) + 1;
73    buf = (char*)calloc(bytes, 1);
74    if (!buf) {
75        return NULL;
76    }
77    memcpy(buf, s, bytes);
78    return buf;
79}
80
81wchar_t* AUI_wcsdup(const wchar_t* s) {
82    size_t bytes = 0;
83    wchar_t* buf = NULL;
84    bytes = (wcslen(s) + 1) * sizeof(wchar_t);
85    buf = (wchar_t*)calloc(bytes, 1);
86    if (!buf) {
87        return NULL;
88    }
89    memcpy(buf, s, bytes);
90    return buf;
91}
92
93typedef struct AUI_DownloadContext_ {
94    double progress;
95    TCHAR* file_path;
96    TCHAR* url;
97    FILE* fp;
98    HINTERNET internet_handle;
99    HANDLE request_complete_event;
100    int url_handle_requested;
101    int wait_request_complete;
102    char buffer[1024];
103    HINTERNET url_handle;
104    int complete;
105    INTERNET_BUFFERS* ib; /* pointer for -Wpadded */
106} AUI_DownloadContext;
107
108const int AUI_CONTEXT_CREATED_SIGNATURE = 0x5aa5f00f;
109
110typedef struct AUI_Context_ {
111    int created_signature;
112    TCHAR* description;
113
114    TCHAR* temp_dir_path;
115    TCHAR* updater_file_path;
116    TCHAR* updater_config_file_path;
117    TCHAR* extracter_file_path;
118    TCHAR* archive_file_path;
119    TCHAR* extracted_dir_path;
120    TCHAR* releases_file_path;
121
122    TCHAR* releases_file_url;
123    TCHAR* archive_file_url;
124
125    unsigned int new_revision;
126    unsigned int current_revision;
127    unsigned int download_debug;
128
129    AUI_DownloadContext* update_information;
130    AUI_DownloadContext* update_file;
131
132    HANDLE extracter_process;
133
134    int found;
135    int ready;
136} AUI_Context;
137
138AU_Result AUI_Extract(AUI_Context* context);
139
140const AU_Result AUI_SUCCESS = {1, 0, AU_NO_ERROR};
141const AU_Result AUI_IO_PENDING = {0, 1, AU_NO_ERROR};
142
143AU_Result E(AU_Error error) {
144    AU_Result result;
145    result.success = 0;
146    result.pending = 0;
147    result.error = error;
148    return result;
149}
150
151#define AUI_GOTO_ERROR_RETURN(error_code) { \
152        memset(&result, 0, sizeof(result)); \
153        result.error = error_code;          \
154        goto clean_up;                      \
155    }
156
157int AUI_IsBadReadPointer(const void* pointer, size_t bytes) {
158    return IsBadReadPtr(pointer, bytes);
159}
160
161int AUI_IsBadWritePointer(void* pointer, size_t bytes) {
162    return IsBadWritePtr(pointer, bytes);
163}
164
165TCHAR* AUI_PathAppendDup(const TCHAR* base, const TCHAR* append) {
166    TCHAR* result = NULL;
167    size_t result_len = 0;
168
169    result_len = _tcslen(base) + 1 + _tcslen(append) + 1;
170    result = (TCHAR*)calloc(result_len * sizeof(TCHAR), 1);
171    if (!result) {
172        return NULL;
173    }
174
175    _tcscpy_s(result, result_len, base);
176    PathAppend(result, append);
177    return result;
178}
179
180char* AUI_WideCharToMultiByteDup(const wchar_t* w) {
181    int bytes = 0;
182    char* m = NULL;
183
184    bytes = WideCharToMultiByte(
185        CP_ACP,
186        0,
187        w,
188        -1,
189        NULL,
190        0,
191        NULL,
192        NULL
193        );
194    if (bytes == 0) {
195        return NULL;
196    }
197
198    m = (char*)calloc((size_t)bytes, 1);
199    if (!m) {
200        return NULL;
201    }
202
203    if (!WideCharToMultiByte(
204        CP_ACP,
205        0,
206        w,
207        -1,
208        m,
209        bytes,
210        NULL,
211        NULL
212        )) {
213            free(m);
214            return NULL;
215    }
216
217    return m;
218}
219
220wchar_t* AUI_MultiByteToWideCharDup(const char* m) {
221    int count = 0;
222    wchar_t* w = NULL;
223
224    count = MultiByteToWideChar(
225        CP_ACP,
226        0,
227        m,
228        -1,
229        NULL,
230        0
231        );
232    if (count == 0) {
233        return NULL;
234    }
235
236    w = (wchar_t*)calloc((size_t)count * sizeof(wchar_t), 1);
237    if (!w) {
238        return NULL;
239    }
240
241    if (!MultiByteToWideChar(
242        CP_ACP,
243        0,
244        m,
245        -1,
246        w,
247        count
248        )) {
249            free(w);
250            return NULL;
251    }
252
253    return w;
254}
255
256#ifdef UNICODE
257BSTR AUI_TCharToBStrDup(const TCHAR* t) {
258    return SysAllocString(t);
259}
260wchar_t* AUI_TCharToWideCharDup(const TCHAR* t) {
261    return AUI_wcsdup(t);
262}
263#else
264BSTR AUI_TCharToBStrDup(const TCHAR* t) {
265    wchar_t* w = AUI_MultiByteToWideCharDup(t);
266    if (!w) {
267        return NULL;
268    }
269    return SysAllocString(w);
270}
271wchar_t* AUI_TCharToWideCharDup(const TCHAR* t) {
272    return AUI_MultiByteToWideCharDup(t);
273}
274#endif
275
276AU_Result AUI_CreateTemporaryDirectory(TCHAR** result) {
277    TCHAR* dir = NULL;
278    TCHAR* system_temp_path = NULL;
279    size_t system_temp_path_chars = 0;
280    TCHAR* temp_path = NULL;
281    unsigned int retry = 0;
282
283    *result = NULL;
284
285    system_temp_path_chars = GetTempPath(0, NULL);
286    system_temp_path = (TCHAR*)calloc(system_temp_path_chars * sizeof(TCHAR), 1);
287    if (!system_temp_path) {
288        LocalFree(dir);
289        return E(AU_ERROR_BAD_ALLOC);
290    }
291    GetTempPath(system_temp_path_chars, system_temp_path);
292
293    for (retry = 5; retry !=0; --retry) {
294        PVOID allocated = NULL;
295        unsigned int args[2];
296
297        args[0] = (unsigned int)GetCurrentThreadId();
298        args[1] = (unsigned int)GetTickCount();
299        if (!FormatMessage(
300            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
301            _T("AutoUpdater.%1!x!.%2!x!"),
302            0,
303            0,
304            (TCHAR*)&allocated,
305            0,
306            (va_list*)args)) {
307                return E(AU_ERROR_BAD_ALLOC);
308        }
309        dir = (TCHAR*)allocated;
310        temp_path = AUI_PathAppendDup(system_temp_path, dir);
311        LocalFree(dir);
312        if (!temp_path) {
313            return E(AU_ERROR_BAD_ALLOC);
314        }
315
316        if (CreateDirectory(temp_path, NULL)) {
317            break;
318        }
319
320        free(temp_path);
321    }
322
323    if (retry == 0) {
324        return E(AU_ERROR_SYSTEM);
325    }
326
327    *result = temp_path;
328    return AUI_SUCCESS;
329}
330
331/* http://support.microsoft.com/kb/238393 */
332HRESULT AUI_AutoWrap(WORD autoType, VARIANT *pvResult, IDispatch *pDisp,
333                     const OLECHAR* ptcName, size_t cArgs, VARIANT* rargs)
334{
335    DISPPARAMS dp = { NULL, NULL, 0, 0 };
336    DISPID dispidNamed = DISPID_PROPERTYPUT;
337    DISPID dispID;
338    HRESULT hr = E_FAIL;
339    VARIANT* args = NULL;
340    OLECHAR* ptName = NULL;
341
342    if(!pDisp) {
343        goto clean_up;
344    }
345
346    ptName = wcsdup(ptcName);
347    if (!ptName) {
348        hr = E_FAIL;
349        goto clean_up;
350    }
351
352    if (pvResult) {
353        VariantInit(pvResult);
354    }
355
356#ifdef __cplusplus
357    hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
358#else
359    hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, &IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
360#endif
361
362    if(FAILED(hr)) {
363        goto clean_up;
364    }
365
366    if (cArgs > 0) {
367        size_t i = 0;
368        args = (VARIANT*)calloc(sizeof(VARIANT) * cArgs, 1);
369        if (!args) {
370            hr = E_FAIL;
371            goto clean_up;
372        }
373        for (i = 0; i < cArgs; ++i) {
374            args[cArgs - i - 1] = rargs[i];
375        }
376    }
377
378    dp.cArgs = cArgs;
379    dp.rgvarg = args;
380
381    if(autoType & DISPATCH_PROPERTYPUT) {
382        dp.cNamedArgs = 1;
383        dp.rgdispidNamedArgs = &dispidNamed;
384    }
385
386#ifdef __cplusplus
387    hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT,
388                       autoType, &dp, pvResult, NULL, NULL);
389#else
390    hr = pDisp->lpVtbl->Invoke(pDisp, dispID, &IID_NULL, LOCALE_USER_DEFAULT,
391                               autoType, &dp, pvResult, NULL, NULL);
392#endif
393
394clean_up:
395    if (args) {
396        free(args);
397    }
398    if (ptName) {
399        free(ptName);
400    }
401    if (cArgs > 0) {
402        size_t i = 0;
403        for (i = 0; i < cArgs; ++i) {
404            VariantClear(&rargs[i]);
405        }
406    }
407    return hr;
408}
409
410
411void AUI_IDispatchAddRef(IDispatch* pd) {
412    if (!pd) {
413        return;
414    }
415#ifdef __cplusplus
416    pd->AddRef();
417#else
418    pd->lpVtbl->AddRef(pd);
419#endif
420}
421
422void AUI_IDispatchRelease(IDispatch* pd) {
423    if (!pd) {
424        return;
425    }
426#ifdef __cplusplus
427    pd->Release();
428#else
429    pd->lpVtbl->Release(pd);
430#endif
431}
432
433AU_Result AUI_Download(AUI_DownloadContext* context) {
434    if (AUI_IsBadWritePointer(context, sizeof(context))) {
435        return E(AU_ERROR_SYSTEM);
436    }
437    /* http://www.codeproject.com/KB/IP/asyncwininet.aspx */
438    if (!context->url_handle && !context->url_handle_requested) {
439        DWORD e = 0;
440        HINTERNET result;
441        context->url_handle_requested = 1;
442        result = InternetOpenUrl(
443            context->internet_handle,
444            context->url,
445            NULL,
446            (DWORD)-1,
447            INTERNET_FLAG_RELOAD,
448            (DWORD_PTR)context);
449
450        e = GetLastError();
451        if (!result && e != ERROR_IO_PENDING) {
452            return E(AU_ERROR_NETWORK);
453        }
454        context->wait_request_complete = 1;
455    }
456
457    if (context->wait_request_complete) {
458        switch (WaitForSingleObject(context->request_complete_event, 0)) {
459        case WAIT_OBJECT_0:
460            break;
461        case WAIT_TIMEOUT:
462            return AUI_IO_PENDING;
463            break;
464        default:
465            return E(AU_ERROR_NETWORK);
466            break;
467        }
468    }
469    context->wait_request_complete = 0;
470
471    if (!context->url_handle) {
472        return E(AU_ERROR_SYSTEM);
473    }
474
475    {
476        DWORD e = 0;
477        BOOL read_result = 0;
478        memset(context->buffer, 0, sizeof(context->buffer));
479        memset(context->ib, 0, sizeof(*context->ib));
480        context->ib->dwStructSize = sizeof(*context->ib);
481        context->ib->lpvBuffer = context->buffer;
482        context->ib->dwBufferLength = sizeof(context->buffer);
483        read_result = InternetReadFileEx(
484            context->url_handle,
485            context->ib,
486            IRF_ASYNC | IRF_USE_CONTEXT | IRF_NO_WAIT,
487            (DWORD_PTR)context);
488        e = GetLastError();
489        if (!read_result && e == ERROR_IO_PENDING) {
490            context->wait_request_complete = 1;
491            return AUI_IO_PENDING;
492        }
493
494        if (!read_result) {
495            return E(AU_ERROR_NETWORK);
496        }
497    }
498    if (context->ib->dwBufferLength == 0) {
499        fclose(context->fp);
500        context->fp = NULL;
501        context->complete = 1;
502        return AUI_SUCCESS;
503    }
504
505    if (fwrite(context->buffer, 1, context->ib->dwBufferLength, context->fp) != context->ib->dwBufferLength) {
506        return E(AU_ERROR_SYSTEM);
507    }
508
509    return AUI_IO_PENDING;
510}
511
512/* http://msdn.microsoft.com/en-us/library/aa385121.aspx */
513void CALLBACK AUI_InternetStatusCallback(
514    HINTERNET hInternet,
515    DWORD_PTR dwContext,
516    DWORD dwInternetStatus,
517    LPVOID lpvStatusInformation,
518    DWORD dwStatusInformationLength) {
519
520    AUI_DownloadContext* context = (AUI_DownloadContext*)dwContext;
521
522    (void)hInternet;
523    (void)dwStatusInformationLength;
524
525    switch (dwInternetStatus) {
526    case INTERNET_STATUS_CLOSING_CONNECTION:
527        /* Closing the connection to the server. The lpvStatusInformation
528           parameter is NULL. */
529        {
530
531        }
532        break;
533    case INTERNET_STATUS_CONNECTED_TO_SERVER:
534        /* Successfully connected to the socket address (SOCKADDR)
535           pointed to by lpvStatusInformation. */
536        {
537
538        }
539        break;
540    case INTERNET_STATUS_CONNECTING_TO_SERVER:
541        /* Connecting to the socket address (SOCKADDR) pointed to by
542           lpvStatusInformation. */
543        {
544        }
545        break;
546    case INTERNET_STATUS_CONNECTION_CLOSED:
547        /* Successfully closed the connection to the server. The
548           lpvStatusInformation parameter is NULL. */
549        {
550        }
551        break;
552#if 0
553    case INTERNET_STATUS_COOKIE_HISTORY:
554        /* Retrieving content from the cache. Contains data about past
555           cookie events for the URL such as if cookies were accepted,
556           rejected, downgraded, or leashed.
557
558           The lpvStatusInformation parameter is a pointer to an
559           InternetCookieHistory structure. */
560        {
561        }
562        break;
563    case INTERNET_STATUS_COOKIE_RECEIVED:
564        /* Indicates the number of cookies that were accepted, rejected,
565           downgraded (changed from persistent to session cookies), or
566           leashed (will be sent out only in 1st party context). The
567           lpvStatusInformation parameter is a DWORD with the number of
568           cookies received. */
569        {
570        }
571        break;
572    case INTERNET_STATUS_COOKIE_SENT:
573        /* Indicates the number of cookies that were either sent or
574           suppressed, when a request is sent. The lpvStatusInformation
575           parameter is a DWORD with the number of cookies sent or
576           suppressed. */
577        {
578        }
579        break;
580#endif
581    case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
582        /* Not implemented. */
583        {
584        }
585        break;
586#if 0
587    case INTERNET_STATUS_DETECTING_PROXY:
588        /* Notifies the client application that a proxy has been detected. */
589        {
590        }
591        break;
592#endif
593    case INTERNET_STATUS_HANDLE_CLOSING:
594        /* This handle value has been terminated. pvStatusInformation
595           contains the address of the handle being closed. The
596           lpvStatusInformation parameter contains the address of the
597           handle being closed. */
598        {
599        }
600        break;
601    case INTERNET_STATUS_HANDLE_CREATED:
602        /* Used by InternetConnect to indicate it has created the new
603           handle. This lets the application call InternetCloseHandle
604           from another thread, if the connect is taking too long. The
605           lpvStatusInformation parameter contains the address of an
606           HINTERNET handle. */
607        {
608            INTERNET_ASYNC_RESULT* async_result = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
609            context->url_handle = (HINTERNET)(async_result->dwResult);
610        }
611        break;
612#if 0
613    case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
614        /* Received an intermediate (100 level) status code message
615           from the server. */
616        {
617        }
618        break;
619#endif
620    case INTERNET_STATUS_NAME_RESOLVED:
621        /* Successfully found the IP address of the name contained in
622           lpvStatusInformation. The lpvStatusInformation parameter points
623           to a PCTSTR containing the host name. */
624        {
625        }
626        break;
627#if 0
628    case INTERNET_STATUS_P3P_HEADER:
629        /* The response has a P3P header in it. */
630        {
631        }
632        break;
633    case INTERNET_STATUS_P3P_POLICYREF:
634        /* Not implemented. */
635        {
636        }
637        break;
638#endif
639    case INTERNET_STATUS_PREFETCH:
640        /* Not implemented. */
641        {
642        }
643        break;
644#if 0
645    case INTERNET_STATUS_PRIVACY_IMPACTED:
646        /* Not implemented. */
647        {
648        }
649        break;
650#endif
651    case INTERNET_STATUS_RECEIVING_RESPONSE:
652        /* Waiting for the server to respond to a request. The
653           lpvStatusInformation parameter is NULL. */
654        {
655        }
656        break;
657    case INTERNET_STATUS_REDIRECT:
658        /* An HTTP request is about to automatically redirect the request.
659           The lpvStatusInformation parameter points to the new URL.
660           At this point, the application can read any data returned
661           by the server with the redirect response and can query the
662           response headers. It can also cancel the operation by closing
663           the handle. This callback is not made if the original request
664           specified INTERNET_FLAG_NO_AUTO_REDIRECT. */
665        {
666        }
667        break;
668    case INTERNET_STATUS_REQUEST_COMPLETE:
669        /* An asynchronous operation has been completed. The
670           lpvStatusInformation parameter contains the address of an
671           INTERNET_ASYNC_RESULT structure. */
672        {
673            SetEvent(context->request_complete_event);
674        }
675        break;
676    case INTERNET_STATUS_REQUEST_SENT:
677        /* Successfully sent the information request to the server. The
678           lpvStatusInformation parameter points to a DWORD value that
679           contains the number of bytes sent. */
680        {
681        }
682        break;
683    case INTERNET_STATUS_RESOLVING_NAME:
684        /* Looking up the IP address of the name contained in
685           lpvStatusInformation. The lpvStatusInformation parameter points
686           to a PCTSTR containing the host name. */
687        {
688        }
689        break;
690    case INTERNET_STATUS_RESPONSE_RECEIVED:
691        /* Successfully received a response from the server. */
692        {
693        }
694        break;
695    case INTERNET_STATUS_SENDING_REQUEST:
696        /* Sending the information request to the server. The
697           lpvStatusInformation parameter is NULL. */
698        {
699        }
700        break;
701#if 0
702    case INTERNET_STATUS_STATE_CHANGE:
703        /* Moved between a secure (HTTPS) and a nonsecure (HTTP) site.
704           The user must be informed of this change; otherwise, the user
705           is at risk of disclosing sensitive information involuntarily.
706           When this flag is set, the lpvStatusInformation parameter points
707           to a status DWORD that contains additional flags. */
708        {
709        }
710        break;
711#endif
712    default:
713        break;
714    }
715}
716
717AU_Result AUI_CreateDownloadContext(AUI_DownloadContext** context_output, const TCHAR* url, const TCHAR* file_path) {
718    AUI_DownloadContext* context = NULL;
719    *context_output = NULL;
720
721    context = (AUI_DownloadContext*)calloc(sizeof(*context), 1);
722    if (!context) {
723        return E(AU_ERROR_BAD_ALLOC);
724    }
725
726    context->ib = (INTERNET_BUFFERS*)calloc(sizeof(*context->ib), 1);
727    if (!context->ib) {
728        return E(AU_ERROR_BAD_ALLOC);
729    }
730
731    context->url = _tcsdup(url);
732    if (!context->url) {
733        return E(AU_ERROR_BAD_ALLOC);
734    }
735
736    context->file_path = _tcsdup(file_path);
737    if (!context->file_path) {
738        return E(AU_ERROR_BAD_ALLOC);
739    }
740
741    _tfopen_s(&context->fp, context->file_path, _T("wb"));
742
743    if (!context->fp) {
744        return E(AU_ERROR_SYSTEM);
745    }
746
747    context->request_complete_event = CreateEvent(NULL, FALSE, FALSE, NULL);
748    if (!context->request_complete_event) {
749        return E(AU_ERROR_SYSTEM);
750    }
751
752    context->internet_handle =
753        InternetOpen(
754        TEXT("saturday06's AutoUpdater"),
755        INTERNET_OPEN_TYPE_PRECONFIG,
756        NULL,
757        NULL,
758        INTERNET_FLAG_ASYNC);
759
760    if (!context->internet_handle) {
761        return E(AU_ERROR_SYSTEM);
762    }
763
764    if (InternetSetStatusCallback(context->internet_handle, AUI_InternetStatusCallback)
765        == INTERNET_INVALID_STATUS_CALLBACK) {
766            return E(AU_ERROR_SYSTEM);
767    }
768
769    *context_output = context;
770
771    return AUI_SUCCESS;
772}
773
774AU_Result AUI_DestroyDownloadContext(AUI_DownloadContext* context) {
775    if (AUI_IsBadReadPointer(context, sizeof(context)) ||
776        AUI_IsBadWritePointer(context, sizeof(context))) {
777            return E(AU_ERROR_SYSTEM);
778    }
779    if (context->ib) {
780        free(context->ib);
781    }
782    if (context->fp) {
783        fclose(context->fp);
784    }
785    if (context->internet_handle) {
786        InternetCloseHandle(context->internet_handle);
787    }
788    if (context->request_complete_event) {
789        CloseHandle(context->request_complete_event);
790    }
791    if (context->url_handle) {
792        InternetCloseHandle(context->url_handle);
793    }
794    if (context->url) {
795        free(context->url);
796    }
797    if (context->file_path) {
798        free(context->file_path);
799    }
800    memset(context, 0, sizeof(context));
801    free(context);
802    return AUI_SUCCESS;
803}
804
805/*
806DWORD CALLBACK AUI_WindowsUpdateThread(LPVOID context_) {
807AUI_UpdaterThread((AUI_Context*)context_);
808return 0;
809}
810*/
811
812AU_Result AUI_CreateContext(AUI_Context* context, const AU_Option* option) {
813    AU_Result r;
814    if (!option   ||
815        !(option->url)  ||
816        AUI_IsBadReadPointer(option, sizeof(*option)) ||
817        AUI_IsBadReadPointer(option->url, 1)) {
818            return E(AU_ERROR_INVALID_OPTION);
819    }
820
821    {
822        HRESULT hr = CoInitialize(NULL);
823        switch (hr)
824        {
825        case S_OK :
826            break;
827        case S_FALSE:
828            /* CoInitialize: The COM library is already initialized on this thread */
829            break;
830        case RPC_E_CHANGED_MODE:
831            /* CoInitialize: A previous call to CoInitializeEx specified the concurrency model for this thread as multithread apartment (MTA). If running Windows 2000, this could also mean that a change from neutral-threaded apartment to single-threaded apartment occurred. */
832            break;
833        default:
834            break;
835        }
836    }
837
838    r = AUI_CreateTemporaryDirectory(&context->temp_dir_path);
839    if (r.error) {
840        return r;
841    }
842    context->updater_file_path   = AUI_PathAppendDup(context->temp_dir_path, _T("AutoUpdater.exe"));
843    if (!context->updater_file_path) {
844        return E(AU_ERROR_BAD_ALLOC);
845    }
846    context->updater_config_file_path   = AUI_PathAppendDup(context->temp_dir_path, _T("AutoUpdater.txt"));
847    if (!context->updater_config_file_path) {
848        return E(AU_ERROR_BAD_ALLOC);
849    }
850    context->extracter_file_path = AUI_PathAppendDup(context->temp_dir_path, _T("Extracter.exe"));
851    if (!context->extracter_file_path) {
852        return E(AU_ERROR_BAD_ALLOC);
853    }
854    context->archive_file_path   = AUI_PathAppendDup(context->temp_dir_path, _T("ArchiveFile"));
855    if (!context->archive_file_path) {
856        return E(AU_ERROR_BAD_ALLOC);
857    }
858    context->extracted_dir_path  = AUI_PathAppendDup(context->temp_dir_path, _T("Extracted"));
859    if (!context->extracted_dir_path) {
860        return E(AU_ERROR_BAD_ALLOC);
861    }
862    context->releases_file_path  = AUI_PathAppendDup(context->temp_dir_path, _T("Releases.xml"));
863    if (!context->releases_file_path) {
864        return E(AU_ERROR_BAD_ALLOC);
865    }
866    context->releases_file_url = _tcsdup(option->url);
867    if (!context->releases_file_url) {
868        return E(AU_ERROR_BAD_ALLOC);
869    }
870    context->current_revision = option->current_revision;
871    context->download_debug = 0;
872    return AUI_SUCCESS;
873}
874
875void AUI_DeleteDirectory(const TCHAR* dir) {
876    TCHAR* glob = NULL;
877    WIN32_FIND_DATA find_data;
878    HANDLE hFind = NULL;
879
880    if (!dir) {
881        return;
882    }
883
884    glob = AUI_PathAppendDup(dir, _T("*"));
885    hFind = FindFirstFile(glob, &find_data);
886    if( INVALID_HANDLE_VALUE == hFind ) {
887        return;
888    }
889
890    do {
891        TCHAR* next_dir = NULL;
892
893        if (CompareString(LOCALE_USER_DEFAULT, 0, find_data.cFileName, -1, _T("."), -1) == CSTR_EQUAL) {
894            continue;
895        }
896
897        if (CompareString(LOCALE_USER_DEFAULT, 0, find_data.cFileName, -1, _T(".."), -1) == CSTR_EQUAL) {
898            continue;
899        }
900
901        next_dir = AUI_PathAppendDup(dir, find_data.cFileName);
902
903        if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) {
904            AUI_DeleteDirectory(next_dir);
905        } else {
906            DeleteFile(next_dir);
907        }
908
909        free(next_dir);
910
911    } while(FindNextFile(hFind, &find_data));
912
913    FindClose(hFind);
914    free(glob);
915    return;
916}
917
918AU_Result AUI_DestroyContext(AUI_Context* context) {
919    context->created_signature = 0;
920
921    if (!context->ready) {
922        AUI_DeleteDirectory(context->temp_dir_path);
923    }
924
925    if (context->temp_dir_path) {
926        free(context->temp_dir_path);
927    }
928    if (context->description) {
929        free(context->description);
930    }
931    if (context->releases_file_url) {
932        free(context->releases_file_url);
933    }
934    if (context->update_information) {
935        AUI_DestroyDownloadContext(context->update_information);
936    }
937    if (context->update_file) {
938        AUI_DestroyDownloadContext(context->update_file);
939    }
940    if (context->extracter_process) {
941        WaitForSingleObject(context->extracter_process, INFINITE);
942        CloseHandle(context->extracter_process);
943    }
944    if (context->updater_file_path) {
945        free(context->updater_file_path);
946    }
947    if (context->extracter_file_path) {
948        free(context->extracter_file_path);
949    }
950    if (context->archive_file_path) {
951        free(context->archive_file_path);
952    }
953    if (context->extracted_dir_path) {
954        free(context->extracted_dir_path);
955    }
956
957    memset(context, 0, sizeof(*context));
958
959    free(context);
960    return AUI_SUCCESS;
961}
962
963AU_Result AUI_ReadReleasesFile(AUI_Context* context) {
964    AU_Result r;
965    HRESULT result = S_OK;
966    VARIANT returnValue;
967    VARIANT args[10];
968    IDispatch* Microsoft_XMLDOM = NULL;
969    IDispatch* documentElement = NULL;
970    IDispatch* releaseNode = NULL;
971    IDispatch* urlNode = NULL;
972    /* IDispatch* descriptionNode = NULL; */
973
974    r = E(AU_ERROR_SYSTEM);
975
976    /* var Microsoft_XMLDOM = new ActiveXObject("Microsoft.XMLDOM") */
977    {
978        OLECHAR progid[] = OLESTR("Microsoft.XMLDOM");
979        CLSID clsid;
980
981        result = CLSIDFromString(progid, &clsid);
982
983        switch (result)
984        {
985        case NOERROR:
986            /* The CLSID was obtained successfully. */
987            break;
988        case E_INVALIDARG:
989            goto clean_up;
990            break;
991        case CO_E_CLASSSTRING:
992            /* The class string was improperly formatted. */
993            goto clean_up;
994            break;
995        case REGDB_E_CLASSNOTREG:
996            /* The CLSID corresponding to the class string was not found in the registry. */
997            goto clean_up;
998            break;
999        case REGDB_E_READREGDB:
1000            /* The registry could not be opened for reading. */
1001            goto clean_up;
1002            break;
1003        default:
1004            goto clean_up;
1005            break;
1006        }
1007
1008#ifdef __cplusplus
1009        result = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (LPVOID*)&Microsoft_XMLDOM);
1010#else
1011        {
1012            /* warning by strict aliasing rule: CoCreateInstance(...., (PVOID*)&Microsoft_XMLDOM); */
1013            PVOID cast = 0;
1014            result = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, &cast);
1015            Microsoft_XMLDOM = (IDispatch*)cast;
1016        }
1017#endif
1018
1019        switch (result)
1020        {
1021        case S_OK:
1022            break;
1023        case REGDB_E_CLASSNOTREG:
1024            goto clean_up;
1025            break;
1026        case CLASS_E_NOAGGREGATION:
1027            /* This class cannot be created as part of an aggregate */
1028            goto clean_up;
1029            break;
1030        case E_NOINTERFACE:
1031            /* The specified class does not implement the requested interface, or the controlling IUnknown does not expose the requested interface */
1032            goto clean_up;
1033            break;
1034        default:
1035            /* unknown error: */
1036            goto clean_up;
1037            break;
1038        }
1039
1040        if (Microsoft_XMLDOM == NULL) {
1041            goto clean_up;
1042        }
1043    }
1044
1045
1046    { /* Microsoft_XMLDOM.async = false */
1047        args[0].vt = VT_BOOL;
1048        args[0].boolVal = VARIANT_FALSE;
1049        result = AUI_AutoWrap(DISPATCH_PROPERTYPUT, NULL, Microsoft_XMLDOM, OLESTR("async"), 1, args);
1050    }
1051
1052
1053    { /* Microsoft_XMLDOM.load("file.xml") */
1054        args[0].vt = VT_BSTR;
1055        args[0].bstrVal = AUI_TCharToBStrDup(context->releases_file_path);
1056        result = AUI_AutoWrap(DISPATCH_METHOD, &returnValue, Microsoft_XMLDOM, OLESTR("load"), 1, args);
1057        if (FAILED(result)) {
1058            goto clean_up;
1059        }
1060        if (!returnValue.boolVal) {
1061            goto clean_up;
1062        }
1063        VariantClear(&returnValue);
1064    }
1065
1066    { /* Microsoft_XMLDOM.setProperty("SelectionLanguage", "XPath"); */
1067        args[0].vt = VT_BSTR;
1068        args[0].bstrVal = SysAllocString(OLESTR("SelectionLanguage"));
1069        if (!args[0].bstrVal) {
1070            goto clean_up;
1071        }
1072        args[1].vt = VT_BSTR;
1073        args[1].bstrVal = SysAllocString(OLESTR("XPath"));
1074        if (!args[1].bstrVal) {
1075            goto clean_up;
1076        }
1077        result = AUI_AutoWrap(DISPATCH_METHOD, NULL, Microsoft_XMLDOM, OLESTR("setProperty"), 2, args);
1078        if (FAILED(result)) {
1079            goto clean_up;
1080        }
1081    }
1082
1083    { /* var documentElement = Microsoft_XMLDOM.documentElement */
1084        result = AUI_AutoWrap(DISPATCH_PROPERTYGET, &returnValue, Microsoft_XMLDOM, OLESTR("documentElement"), 0, NULL);
1085        if (FAILED(result)) {
1086            goto clean_up;
1087        }
1088        if (!returnValue.pdispVal) {
1089            goto clean_up;
1090        }
1091        documentElement = returnValue.pdispVal;
1092        AUI_IDispatchAddRef(documentElement);
1093        VariantClear(&returnValue);
1094    }
1095
1096    { /* var releaseNode = documentElement.selectSingleNode("..."); */
1097        TCHAR xpath[500];
1098        memset(xpath, 0, sizeof(xpath));
1099        wsprintf(xpath, /* TODO */
1100            _T("/releases/release[not(@debug) or @debug <= %d][")
1101            _T("not(@revision <= preceding-sibling::release[not(@debug) or @debug <= %d]/@revision) and ")
1102            _T("not(@revision <= following-sibling::release[not(@debug) or @debug <= %d]/@revision)]")
1103            , context->download_debug, context->download_debug, context->download_debug
1104            );
1105
1106        args[0].vt = VT_BSTR;
1107        args[0].bstrVal = AUI_TCharToBStrDup(xpath);
1108        if (!args[0].bstrVal) {
1109            goto clean_up;
1110        }
1111        result = AUI_AutoWrap(DISPATCH_METHOD, &returnValue, Microsoft_XMLDOM, OLESTR("selectSingleNode"), 1, args);
1112        if (FAILED(result)) {
1113            goto clean_up;
1114        }
1115        if (!returnValue.pdispVal) {
1116            goto clean_up;
1117        }
1118        releaseNode = returnValue.pdispVal;
1119        AUI_IDispatchAddRef(releaseNode);
1120        VariantClear(&returnValue);
1121    }
1122
1123    { /* var urlNode = releaseNode.selectSingleNode("url"); */
1124        args[0].vt = VT_BSTR;
1125        args[0].bstrVal = SysAllocString(OLESTR("url"));
1126        if (!args[0].bstrVal) {
1127            goto clean_up;
1128        }
1129        result = AUI_AutoWrap(DISPATCH_METHOD, &returnValue, releaseNode, OLESTR("selectSingleNode"), 1, args);
1130        if (FAILED(result)) {
1131            goto clean_up;
1132        }
1133        if (!returnValue.pdispVal) {
1134            goto clean_up;
1135        }
1136        urlNode = returnValue.pdispVal;
1137        AUI_IDispatchAddRef(urlNode);
1138        VariantClear(&returnValue);
1139    }
1140
1141    { /* urlNode.text; */
1142        result = AUI_AutoWrap(DISPATCH_PROPERTYGET, &returnValue, urlNode, OLESTR("text"), 0, NULL);
1143        if (FAILED(result)) {
1144            goto clean_up;
1145        }
1146        if (!returnValue.bstrVal) {
1147            goto clean_up;
1148        }
1149
1150#ifdef UNICODE
1151        context->archive_file_url = wcsdup(returnValue.bstrVal);
1152#else
1153        context->archive_file_url = AUI_WideCharToMultiByteDup(returnValue.bstrVal);
1154#endif
1155
1156        VariantClear(&returnValue);
1157        if (!context->archive_file_path) {
1158            goto clean_up;
1159        }
1160        context->found = 1;
1161    }
1162
1163    r = AUI_SUCCESS;
1164clean_up:
1165    AUI_IDispatchRelease(urlNode);
1166    AUI_IDispatchRelease(releaseNode);
1167    AUI_IDispatchRelease(Microsoft_XMLDOM);
1168
1169    return r;
1170}
1171
1172AU_Result AUI_GetInformation(AUI_Context* context, AU_Information* information) {
1173    AU_Result r;
1174    memset(information, 0, sizeof(*information));
1175    if (!context->update_information) {
1176        r = AUI_CreateDownloadContext(
1177            &context->update_information,
1178            context->releases_file_url,
1179            context->releases_file_path);
1180        if (r.error) {
1181            return r;
1182        }
1183    }
1184
1185    if (!context->update_information->complete) {
1186        r = AUI_Download(context->update_information);
1187        if (!r.success) {
1188            return r;
1189        }
1190    }
1191
1192    r = AUI_ReadReleasesFile(context);
1193    information->found = context->found;
1194
1195    return r;
1196}
1197
1198extern const size_t AUI_UPDATER_BYTES;
1199extern const char AUI_UPDATER_DATA[];
1200
1201AU_Result AUI_StartUpdate(AUI_Context* context) {
1202    AU_Result r;
1203    if (!context->found) {
1204        AU_Information i;
1205        r = AUI_GetInformation(context, &i);
1206        if (!r.success) {
1207            return r;
1208        }
1209    }
1210
1211    if (!context->update_file) {
1212        r = AUI_CreateDownloadContext(
1213            &context->update_file,
1214            context->archive_file_url,
1215            context->archive_file_path);
1216        if (r.error) {
1217            return r;
1218        }
1219    }
1220
1221    if (!context->update_file->complete) {
1222        r = AUI_Download(context->update_file);
1223        if (!r.success) {
1224            return r;
1225        }
1226    }
1227
1228    AUI_Extract(context);
1229
1230    {
1231        FILE* f = NULL;
1232        r = E(AU_ERROR_SYSTEM);
1233        _tfopen_s(&f, context->updater_file_path, _T("wb"));
1234        if (!f) {
1235            return r;
1236        }
1237        fwrite(AUI_UPDATER_DATA, AUI_UPDATER_BYTES, 1, f);
1238        fclose(f);
1239    }
1240
1241    {
1242        TCHAR myname[500];
1243        TCHAR cd[500];
1244        TCHAR* target = NULL;
1245        FILE* f = NULL;
1246        GetCurrentDirectory(sizeof(cd) / sizeof(cd[0]), cd);
1247        GetModuleFileName(GetModuleHandle(NULL), myname, sizeof(myname) / sizeof(myname[0]) - 1);
1248        target = _tcsdup(myname);
1249        if (!target) {
1250            return E(AU_ERROR_SYSTEM);
1251        }
1252        PathRemoveFileSpec(target);
1253
1254        _tfopen_s(&f, context->updater_config_file_path, _T("wb"));
1255        if (!f) {
1256            return E(AU_ERROR_SYSTEM);
1257        }
1258
1259        {
1260            wchar_t* data = NULL;
1261            PVOID allocated = NULL;
1262            DWORD_PTR args[6];
1263
1264            args[0] = (DWORD_PTR)GetCurrentProcessId();
1265            args[1] = (DWORD_PTR)context->extracted_dir_path;
1266            args[2] = (DWORD_PTR)target;
1267            args[3] = (DWORD_PTR)myname;
1268            args[4] = (DWORD_PTR)cd;
1269            args[5] = (DWORD_PTR)context->temp_dir_path;
1270
1271            if (!FormatMessage(
1272                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
1273                _T("DUMMY_LINE_FOR_BOM\r\n")
1274                _T("%1!d!\r\n")
1275                _T("%2!s!\r\n")
1276                _T("%3!s!\r\n")
1277                _T("%4!s!\r\n")
1278                _T("%5!s!\r\n")
1279                _T("%6!s!\r\n"),
1280                0,
1281                0,
1282                (TCHAR*)&allocated,
1283                0,
1284                (va_list*)args)) {
1285                    return E(AU_ERROR_BAD_ALLOC);
1286            }
1287            data = AUI_TCharToWideCharDup((TCHAR*)allocated);
1288            LocalFree(allocated);
1289            if (!data) {
1290                return E(AU_ERROR_BAD_ALLOC);
1291            }
1292            if (!fwrite(data, (wcslen(data) + 1) * sizeof(data[0]), 1, f)) {
1293                free(data);
1294                fclose(f);
1295                return E(AU_ERROR_SYSTEM);
1296            }
1297        }
1298        fclose(f);
1299    }
1300
1301    {
1302        STARTUPINFO si;
1303        PROCESS_INFORMATION pi;
1304        memset(&si, 0, sizeof(si));
1305        si.cb = sizeof(si);
1306        memset(&pi, 0, sizeof(pi));
1307        CreateProcess(context->updater_file_path, NULL, NULL, NULL, FALSE, 0, NULL, context->temp_dir_path, &si, &pi);
1308        CloseHandle(pi.hThread);
1309        CloseHandle(pi.hProcess);
1310    }
1311
1312    context->ready = 1;
1313
1314    return AUI_SUCCESS;
1315}
1316
1317AU_Result AUI_Cancel(AUI_Context* context) {
1318    (void)context;
1319    return AUI_SUCCESS;
1320}
1321
1322AU_Result AUI_ContextToInternalContext(AUI_Context** internal_context_output, const AU_Context context) {
1323    AU_Result result;
1324    AUI_Context* internal_context = (AUI_Context*)context;
1325    *internal_context_output = 0;
1326
1327    if (AUI_IsBadReadPointer(internal_context, sizeof(internal_context))) {
1328        AUI_GOTO_ERROR_RETURN(AU_ERROR_INVALID_CONTEXT);
1329    }
1330
1331    if (AUI_IsBadWritePointer(internal_context, sizeof(internal_context))) {
1332        AUI_GOTO_ERROR_RETURN(AU_ERROR_INVALID_CONTEXT);
1333    }
1334
1335    if (internal_context->description != NULL && AUI_IsBadWritePointer(internal_context->description, 1)) {
1336        AUI_GOTO_ERROR_RETURN(AU_ERROR_INVALID_CONTEXT);
1337    }
1338
1339    if (internal_context->releases_file_path != NULL && AUI_IsBadWritePointer(internal_context->releases_file_path, 1)) {
1340        AUI_GOTO_ERROR_RETURN(AU_ERROR_INVALID_CONTEXT);
1341    }
1342
1343    if (internal_context->created_signature != AUI_CONTEXT_CREATED_SIGNATURE) {
1344        AUI_GOTO_ERROR_RETURN(AU_ERROR_INVALID_CONTEXT);
1345    }
1346
1347    *internal_context_output = internal_context;
1348
1349    result = AUI_SUCCESS;
1350clean_up:
1351   
1352    return result;
1353}
1354
1355/* -----------------------------------------------------------------------
1356Interfaces
1357----------------------------------------------------------------------- */
1358
1359#define AUI_API_PRECONDITION                       \
1360    AUI_Context* internal_context = NULL;          \
1361{                                              \
1362    AU_Result convert_result = AUI_ContextToInternalContext(&internal_context, context);  \
1363    if (convert_result.error) {                \
1364    return convert_result;                 \
1365    }                                          \
1366}
1367
1368AU_Result AU_CreateContext(AU_Context* pcontext, const AU_Option* option) {
1369    AU_Context context = NULL;
1370
1371    if (AUI_IsBadWritePointer(pcontext, sizeof(*pcontext))) {
1372        return E(AU_ERROR_INVALID_CONTEXT);
1373    }
1374
1375    {
1376        AUI_Context* internal_context = (AUI_Context*)calloc(sizeof(AUI_Context), 1);
1377        if (!internal_context) {
1378            return E(AU_ERROR_BAD_ALLOC);
1379        }
1380        internal_context->created_signature = AUI_CONTEXT_CREATED_SIGNATURE;
1381        context = (AU_Context)internal_context;
1382    }
1383
1384    {
1385        AU_Result result;
1386        AUI_API_PRECONDITION;
1387        result = AUI_CreateContext(internal_context, option);
1388        if (result.success) {
1389            *pcontext = context;
1390        }
1391        return result;
1392    }
1393}
1394
1395AU_Result AU_DestroyContext(AU_Context context) {
1396    AUI_API_PRECONDITION;
1397    return AUI_DestroyContext(internal_context);
1398}
1399
1400AU_Result AU_GetInformation(AU_Context context, AU_Information* infomation) {
1401    AUI_API_PRECONDITION;
1402    return AUI_GetInformation(internal_context, infomation);
1403}
1404
1405AU_Result AU_StartUpdate(AU_Context context) {
1406    AUI_API_PRECONDITION;
1407    return AUI_StartUpdate(internal_context);
1408}
1409
1410AU_Result AU_Cancel(AU_Context context) {
1411    AUI_API_PRECONDITION;
1412    return AUI_Cancel(internal_context);
1413}
1414
1415void InternalTest(void) {
1416    AU_Result r;
1417    AUI_DownloadContext* context;
1418
1419    r = AUI_CreateDownloadContext(
1420        &context,
1421        _T("http://upload.wikimedia.org/wikipedia/commons/b/bd/Rondo_Alla_Turka.ogg"),
1422        _T("C:\\temp\\bbb.ogg"));
1423
1424    if (r.error) {
1425        abort();
1426    }
1427
1428    do {
1429        r = AUI_Download(context);
1430    } while (r.pending);
1431
1432    AUI_DestroyDownloadContext(context);
1433
1434    /* context-> */
1435}
1436
1437#include "XDef.h"
1438#include "Copy.inl"
1439#include "extracter/7za/7za.h"
1440#include "extracter/7za/7za.inl"
1441
Note: See TracBrowser for help on using the browser.