fixed GTKHTML detection
[k8lowj.git] / src / network-win32.c
blob5799562223c7460946f823b3a2e7e20b9bc85ee7
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
6 #ifdef WINDOZE
8 #include "gtk-all.h"
10 #include <windows.h>
11 #include <wininet.h>
12 #include <io.h>
13 #include <fcntl.h>
15 #include "protocol.h"
16 #include "conf.h"
17 #include "util.h"
18 #include "network-internal.h"
20 /* InternetSetStatusCallback is pretty much useless for sync sockets.
21 * XXX should we change to async sockets? IIRC they're nothing but trouble.
22 * for now, not worth the effort. */
23 #undef USE_INTERNET_STATUS_CALLBACK
25 static void
26 net_getlasterror(GError **err) {
27 LPVOID lpMsgBuf;
28 HMODULE hModule = NULL;
29 DWORD errcode = GetLastError();
31 if (errcode > INTERNET_ERROR_BASE && errcode <= INTERNET_ERROR_LAST)
32 hModule = LoadLibraryEx("wininet.dll", NULL,
33 LOAD_LIBRARY_AS_DATAFILE);
35 if (!FormatMessage(
36 FORMAT_MESSAGE_ALLOCATE_BUFFER |
37 FORMAT_MESSAGE_IGNORE_INSERTS |
38 (hModule ?
39 FORMAT_MESSAGE_FROM_HMODULE :
40 FORMAT_MESSAGE_FROM_SYSTEM),
41 hModule,
42 errcode,
43 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
44 (LPTSTR) &lpMsgBuf,
46 NULL )) {
47 g_set_error(err, 0, 0, "Unable to lookup error code.");
48 } else {
49 g_set_error(err, 0, 0, lpMsgBuf);
51 LocalFree(lpMsgBuf);
54 #ifdef USE_INTERNET_STATUS_CALLBACK
55 static void CALLBACK
56 net_internetstatus_cb(HINTERNET hInternet, DWORD_PTR dwContext,
57 DWORD dwInternetStatus, LPVOID lpStatusInformation,
58 DWORD dwStatusLength) {
60 g_print("status %ld\n", dwInternetStatus);
62 #endif /* USE_INTERNET_STATUS_CALLBACK */
64 static GString *
65 readresponse(HINTERNET hRequest,
66 NetStatusCallback cb, gpointer data,
67 GError **err) {
68 char buf[READ_BLOCK_SIZE];
69 DWORD idx, len;
70 NetStatusProgress progress = {0};
71 GString *response;
73 idx = 0;
74 len = sizeof(DWORD);
75 HttpQueryInfo(hRequest,
76 HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
77 &progress.total, &len, NULL);
79 if (cb) cb(NET_STATUS_PROGRESS, &progress, data);
81 response = g_string_sized_new(READ_BLOCK_SIZE);
82 len = READ_BLOCK_SIZE;
83 while (InternetReadFile(hRequest, buf, READ_BLOCK_SIZE, &len) &&
84 len > 0) {
85 g_string_append_len(response, buf, len);
86 progress.current += len;
87 if (cb) cb(NET_STATUS_PROGRESS, &progress, data);
88 len = READ_BLOCK_SIZE;
90 if (len != 0) {
91 net_getlasterror(err);
92 g_string_free(response, TRUE);
93 return NULL;
95 if (progress.total > 0 && progress.current < progress.total) {
96 g_set_error(err, 0, 0, _("The connection was closed."));
97 g_string_free(response, TRUE);
98 return NULL;
101 return response;
104 GString*
105 net_post_blocking(const char *surl, GSList *headers, GString *post,
106 NetStatusCallback cb, gpointer data,
107 GError **err) {
108 HINTERNET hInternet, hConnection, hRequest;
109 GString *response = NULL;
110 char *url, *host;
111 URL_COMPONENTS urlc = {0};
113 url = g_strdup(surl);
114 urlc.dwStructSize = sizeof(URL_COMPONENTS);
115 urlc.lpszHostName = NULL;
116 urlc.dwHostNameLength = (DWORD)strlen(url);
117 if (!InternetCrackUrl(url, urlc.dwHostNameLength, 0, &urlc)) {
118 net_getlasterror(err);
119 g_free(url);
120 return NULL;
122 /* host is a pointer to a place within url;
123 we modify this string directly, and just free
124 url when we're done. */
125 host = urlc.lpszHostName;
126 host[urlc.dwHostNameLength] = 0;
128 if ((hInternet = InternetOpen("LogJam-Win32",
129 INTERNET_OPEN_TYPE_PRECONFIG,
130 NULL, NULL, 0)) != NULL) {
132 #ifdef USE_INTERNET_STATUS_CALLBACK
133 if (!InternetSetStatusCallback(hInternet, net_internetstatus_cb)) {
134 net_getlasterror(err);
137 if ((hConnection = InternetConnect(hInternet,
138 host, urlc.nPort,
139 NULL, NULL,
140 INTERNET_SERVICE_HTTP, 0, 0xDEADBEEF)) != NULL) {
141 #else /* !USE_INTERNET_STATUS_CALLBACK */
142 if ((hConnection = InternetConnect(hInternet,
143 host, urlc.nPort,
144 NULL, NULL,
145 INTERNET_SERVICE_HTTP, 0, 0)) != NULL) {
146 #endif /* USE_INTERNET_STATUS_CALLBACK */
148 if ((hRequest = HttpOpenRequest(hConnection,
149 "POST", "/interface/flat",
150 NULL, NULL, NULL, 0, 0)) != NULL) {
152 if (HttpSendRequest(hRequest, NULL, 0,
153 post->str, post->len)) {
154 response = readresponse(hRequest, cb, data, err);
155 } else {
156 net_getlasterror(err);
158 InternetCloseHandle(hRequest);
159 } else {
160 net_getlasterror(err);
162 InternetCloseHandle(hConnection);
163 } else {
164 net_getlasterror(err);
166 InternetCloseHandle(hInternet);
167 } else {
168 net_getlasterror(err);
170 g_free(url);
172 return response;
175 typedef struct {
176 GMainLoop *mainloop;
177 GSource *source;
178 int pipe;
179 const char *url;
180 GSList *headers;
181 GString *post;
182 GError **err;
183 GString *response;
184 NetStatusCallback user_cb;
185 gpointer user_data;
186 } ThreadData;
188 void status_to_pipe_cb(NetStatusType status,
189 gpointer statusdata, gpointer data) {
190 ThreadData *threaddata = (ThreadData*)data;
191 char t = status;
192 switch (status) {
193 case NET_STATUS_PROGRESS:
194 write(threaddata->pipe, &t, 1);
195 write(threaddata->pipe, statusdata, sizeof(NetStatusProgress));
196 break;
197 default:
198 g_warning("status_to_pipe_cb: unknown status %d.\n", status);
202 static DWORD WINAPI
203 net_post_threadproc(LPVOID param) {
204 ThreadData *threaddata = (ThreadData*)param;
205 char t = NET_STATUS_DONE;
206 threaddata->response =
207 net_post_blocking(threaddata->url,
208 threaddata->headers, threaddata->post,
209 status_to_pipe_cb, threaddata,
210 threaddata->err);
211 write(threaddata->pipe, &t, 1);
212 return 0;
215 static gboolean
216 net_post_input_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
217 ThreadData *threaddata = (ThreadData*)data;
218 char t;
219 NetStatusProgress progress;
221 GDK_THREADS_ENTER();
222 g_io_channel_read_chars(source, &t, 1, NULL, NULL);
223 switch (t) {
224 case NET_STATUS_PROGRESS:
225 g_io_channel_read_chars(source, (gchar*)&progress,
226 sizeof(NetStatusProgress), NULL, NULL);
227 threaddata->user_cb(NET_STATUS_PROGRESS, &progress,
228 threaddata->user_data);
229 break;
230 case NET_STATUS_DONE:
231 g_source_destroy(threaddata->source);
232 g_main_loop_quit(threaddata->mainloop);
233 break;
234 default:
235 g_warning("net_post_input_cb: unknown status %d.\n", t);
237 GDK_THREADS_LEAVE();
238 if (t == NET_STATUS_DONE)
239 return FALSE;
240 return TRUE;
243 GString*
244 net_post_mainloop(const char *url, GSList *headers, GString *post,
245 NetStatusCallback cb, gpointer data,
246 GError **err) {
247 ThreadData threaddata = {0};
248 GMainLoop *mainloop;
249 GIOChannel *channel;
250 GSource *source;
251 DWORD threadid;
252 int fds[2];
254 if (_pipe(fds, 4096, _O_BINARY) < 0)
255 perror("pipe");
257 mainloop = g_main_loop_new(NULL, TRUE);
259 // XXX http://mail.gnome.org/archives/gtk-devel-list/2003-June/msg00102.html
260 channel = g_io_channel_unix_new(fds[0]);
261 g_io_channel_set_encoding(channel, NULL, NULL);
262 g_io_channel_set_buffered(channel, FALSE);
263 source = g_io_create_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
264 g_source_set_callback(source,
265 (GSourceFunc)net_post_input_cb,
266 &threaddata, NULL);
267 g_source_attach(source, NULL);
269 threaddata.mainloop = mainloop;
270 threaddata.source = source;
271 threaddata.pipe = fds[1];
272 threaddata.headers = headers;
273 threaddata.post = post;
274 threaddata.url = url;
275 threaddata.err = err;
276 threaddata.user_cb = cb;
277 threaddata.user_data = data;
279 g_print("starting thread\n");
281 CreateThread(NULL, 0, net_post_threadproc, &threaddata, 0, &threadid);
283 g_main_loop_run(mainloop);
285 /* XXX: closing these in the opposite order causes deadlocks? */
286 close(fds[1]);
287 close(fds[0]);
289 g_io_channel_unref(channel);
290 /* source is destroyed in the callback. */
291 g_main_loop_unref(mainloop);
293 return threaddata.response;
296 void
297 net_mainloop_cancel(NetMainloopHandle handle) {
298 /* XXX implement me. */
301 #endif