Don't write the Content-Length header ourselves, WinHttpSendRequest()
[platform/upstream/glib.git] / gio / win32 / gwinhttpfile.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  * Copyright (C) 2008 Novell, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Author: Alexander Larsson <alexl@redhat.com>
22  * Author: Tor Lillqvist <tml@novell.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include "gfile.h"
30 #include "gfileattribute.h"
31 #include "gwinhttpfile.h"
32 #include "gwinhttpfileinputstream.h"
33 #include "gwinhttpfileoutputstream.h"
34 #include "gioerror.h"
35
36 #include "glibintl.h"
37
38 #include "gioalias.h"
39
40 static void g_winhttp_file_file_iface_init (GFileIface *iface);
41
42 #define g_winhttp_file_get_type _g_winhttp_file_get_type
43 G_DEFINE_TYPE_WITH_CODE (GWinHttpFile, g_winhttp_file, G_TYPE_OBJECT,
44                          G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
45                                                 g_winhttp_file_file_iface_init))
46
47 static void
48 g_winhttp_file_finalize (GObject *object)
49 {
50   GWinHttpFile *file;
51
52   file = G_WINHTTP_FILE (object);
53
54   g_free (file->url.lpszScheme);
55   g_free (file->url.lpszHostName);
56   g_free (file->url.lpszUserName);
57   g_free (file->url.lpszPassword);
58   g_free (file->url.lpszUrlPath);
59   g_free (file->url.lpszExtraInfo);
60
61   G_OBJECT_CLASS (g_winhttp_file_parent_class)->finalize (object);
62 }
63
64 static void
65 g_winhttp_file_class_init (GWinHttpFileClass *klass)
66 {
67   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
68
69   gobject_class->finalize = g_winhttp_file_finalize;
70 }
71
72 static void
73 g_winhttp_file_init (GWinHttpFile *winhttp)
74 {
75 }
76
77 /**
78  * _g_winhttp_file_new:
79  * @vfs: GWinHttpVfs to use
80  * @uri: URI of the GWinHttpFile to create.
81  * 
82  * Returns: new winhttp #GFile.
83  **/
84 GFile *
85 _g_winhttp_file_new (GWinHttpVfs *vfs,
86                      const char  *uri)
87 {
88   wchar_t *wuri;
89   GWinHttpFile *file;
90
91   wuri = g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL);
92
93   if (wuri == NULL)
94     return NULL;
95
96   file = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
97   file->vfs = vfs;
98
99   memset (&file->url, 0, sizeof (file->url));
100   file->url.dwStructSize = sizeof (file->url);
101   file->url.dwSchemeLength = 1;
102   file->url.dwHostNameLength = 1;
103   file->url.dwUserNameLength = 1;
104   file->url.dwPasswordLength = 1;
105   file->url.dwUrlPathLength = 1;
106   file->url.dwExtraInfoLength = 1;
107
108   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
109     {
110       g_free (wuri);
111       return NULL;
112     }
113
114   file->url.lpszScheme = g_new (wchar_t, ++file->url.dwSchemeLength);
115   file->url.lpszHostName = g_new (wchar_t, ++file->url.dwHostNameLength);
116   file->url.lpszUserName = g_new (wchar_t, ++file->url.dwUserNameLength);
117   file->url.lpszPassword = g_new (wchar_t, ++file->url.dwPasswordLength);
118   file->url.lpszUrlPath = g_new (wchar_t, ++file->url.dwUrlPathLength);
119   file->url.lpszExtraInfo = g_new (wchar_t, ++file->url.dwExtraInfoLength);
120
121   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
122     {
123       g_free (file->url.lpszScheme);
124       g_free (file->url.lpszHostName);
125       g_free (file->url.lpszUserName);
126       g_free (file->url.lpszPassword);
127       g_free (file->url.lpszUrlPath);
128       g_free (file->url.lpszExtraInfo);
129       g_free (wuri);
130       return NULL;
131     }
132   
133   g_free (wuri);
134   return G_FILE (file);
135 }
136
137 static gboolean
138 g_winhttp_file_is_native (GFile *file)
139 {
140   return FALSE;
141 }
142
143 static gboolean
144 g_winhttp_file_has_uri_scheme (GFile      *file,
145                                const char *uri_scheme)
146 {
147   return (g_ascii_strcasecmp (uri_scheme, "http") == 0 ||
148           g_ascii_strcasecmp (uri_scheme, "https") == 0);
149 }
150
151 static char *
152 g_winhttp_file_get_uri_scheme (GFile *file)
153 {
154   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
155
156   return g_utf16_to_utf8 (winhttp_file->url.lpszScheme, -1, NULL, NULL, NULL);
157 }
158
159 static char *
160 g_winhttp_file_get_basename (GFile *file)
161 {
162   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
163   char *basename;
164   char *last_slash;
165   char *retval;
166
167   basename = g_utf16_to_utf8 (winhttp_file->url.lpszUrlPath, -1, NULL, NULL, NULL);
168   last_slash = strrchr (basename, '/');
169   /* If no slash, or only "/" fallback to full path part of URI */
170   if (last_slash == NULL || last_slash[1] == '\0')
171     return basename;
172
173   retval = g_strdup (last_slash + 1);
174   g_free (basename);
175
176   return retval;
177 }
178
179 static char *
180 g_winhttp_file_get_path (GFile *file)
181 {
182   return NULL;
183 }
184
185 static char *
186 g_winhttp_file_get_uri (GFile *file)
187 {
188   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
189   DWORD len;
190   wchar_t *wuri;
191   char *retval;
192
193   len = 0;
194   if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, NULL, &len) &&
195       GetLastError () != ERROR_INSUFFICIENT_BUFFER)
196     return NULL;
197
198   wuri = g_new (wchar_t, ++len);
199
200   if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, wuri, &len))
201     {
202       g_free (wuri);
203       return NULL;
204     }
205
206   retval = g_utf16_to_utf8 (wuri, -1, NULL, NULL, NULL);
207   g_free (wuri);
208
209   return retval;
210 }
211
212 static char *
213 g_winhttp_file_get_parse_name (GFile *file)
214 {
215   /* FIXME: More hair surely needed */
216
217   return g_winhttp_file_get_uri (file);
218 }
219
220 static GFile *
221 g_winhttp_file_get_parent (GFile *file)
222 {
223   GWinHttpFile *winhttp_file;
224   char *uri;
225   char *last_slash;
226   GFile *parent;
227
228   winhttp_file = G_WINHTTP_FILE (file);
229
230   uri = g_winhttp_file_get_uri (file);
231   if (uri == NULL)
232     return NULL;
233     
234   last_slash = strrchr (uri, '/');
235   if (last_slash == NULL || *(last_slash+1) == 0)
236     {
237       g_free (uri);
238       return NULL;
239     }
240
241   while (last_slash > uri && *last_slash == '/')
242     last_slash--;
243
244   last_slash[1] = '\0';
245
246   parent = _g_winhttp_file_new (winhttp_file->vfs, uri);
247   g_free (uri);
248   
249   return parent;
250 }
251
252 static GFile *
253 g_winhttp_file_dup (GFile *file)
254 {
255   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
256   char *uri = g_winhttp_file_get_uri (file);
257   GFile *retval = _g_winhttp_file_new (winhttp_file->vfs, uri);
258
259   g_free (uri);
260
261   return retval;
262 }
263
264 static guint
265 g_winhttp_file_hash (GFile *file)
266 {
267   char *uri = g_winhttp_file_get_uri (file);
268   guint retval = g_str_hash (uri);
269
270   g_free (uri);
271
272   return retval;
273 }
274
275 static gboolean
276 g_winhttp_file_equal (GFile *file1,
277                       GFile *file2)
278 {
279   char *uri1 = g_winhttp_file_get_uri (file1);
280   char *uri2 = g_winhttp_file_get_uri (file2);
281   gboolean retval = g_str_equal (uri1, uri2);
282
283   g_free (uri1);
284   g_free (uri2);
285   
286   return retval;
287 }
288
289 static const char *
290 match_prefix (const char *path, 
291               const char *prefix)
292 {
293   int prefix_len;
294
295   prefix_len = strlen (prefix);
296   if (strncmp (path, prefix, prefix_len) != 0)
297     return NULL;
298   
299   if (prefix_len > 0 && prefix[prefix_len-1] == '/')
300     prefix_len--;
301   
302   return path + prefix_len;
303 }
304
305 static gboolean
306 g_winhttp_file_prefix_matches (GFile *parent,
307                                GFile *descendant)
308 {
309   char *parent_uri = g_winhttp_file_get_uri (parent);
310   char *descendant_uri = g_winhttp_file_get_uri (descendant);
311   const char *remainder;
312   gboolean retval;
313
314   remainder = match_prefix (descendant_uri, parent_uri);
315
316   if (remainder != NULL && *remainder == '/')
317     retval = TRUE;
318   else
319     retval = FALSE;
320
321   g_free (parent_uri);
322   g_free (descendant_uri);
323
324   return retval;
325 }
326
327 static char *
328 g_winhttp_file_get_relative_path (GFile *parent,
329                                   GFile *descendant)
330 {
331   char *parent_uri = g_winhttp_file_get_uri (parent);
332   char *descendant_uri = g_winhttp_file_get_uri (descendant);
333   const char *remainder;
334   char *retval;
335
336   remainder = match_prefix (descendant_uri, parent_uri);
337   
338   if (remainder != NULL && *remainder == '/')
339     retval = g_strdup (remainder + 1);
340   else
341     retval = NULL;
342
343   g_free (parent_uri);
344   g_free (descendant_uri);
345
346   return retval;
347 }
348
349 static GFile *
350 g_winhttp_file_resolve_relative_path (GFile      *file,
351                                       const char *relative_path)
352 {
353   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
354   GWinHttpFile *child;
355   wchar_t *wnew_path = g_utf8_to_utf16 (relative_path, -1, NULL, NULL, NULL);
356
357   if (wnew_path == NULL)
358     return NULL;
359
360   if (*wnew_path != '/')
361     {
362       wchar_t *tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + 1 + wcslen (wnew_path) + 1);
363       wcscpy (tmp, winhttp_file->url.lpszUrlPath);
364       wcscat (tmp, L"/");
365       wcscat (tmp, wnew_path);
366
367       g_free (wnew_path);
368       wnew_path = tmp;
369     }
370
371   child = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
372   child->vfs = winhttp_file->vfs;
373   child->url = winhttp_file->url;
374   child->url.lpszScheme = g_memdup (winhttp_file->url.lpszScheme, winhttp_file->url.dwSchemeLength*2);
375   child->url.lpszHostName = g_memdup (winhttp_file->url.lpszHostName, winhttp_file->url.dwHostNameLength*2);
376   child->url.lpszUserName = g_memdup (winhttp_file->url.lpszUserName, winhttp_file->url.dwUserNameLength*2);
377   child->url.lpszPassword = g_memdup (winhttp_file->url.lpszPassword, winhttp_file->url.dwPasswordLength*2);
378   child->url.lpszUrlPath = wnew_path;
379   child->url.dwUrlPathLength = 2*(wcslen (wnew_path)+1);
380   child->url.lpszExtraInfo = NULL;
381   child->url.dwExtraInfoLength = 0;
382   
383   return (GFile *) child;
384 }
385
386 static GFile *
387 g_winhttp_file_get_child_for_display_name (GFile        *file,
388                                            const char   *display_name,
389                                            GError      **error)
390 {
391   GFile *new_file;
392   char *basename;
393
394   basename = g_locale_from_utf8 (display_name, -1, NULL, NULL, NULL);
395   if (basename == NULL)
396     {
397       g_set_error (error, G_IO_ERROR,
398                    G_IO_ERROR_INVALID_FILENAME,
399                    _("Invalid filename %s"), display_name);
400       return NULL;
401     }
402
403   new_file = g_file_get_child (file, basename);
404   g_free (basename);
405   
406   return new_file;
407 }
408
409 static GFile *
410 g_winhttp_file_set_display_name (GFile         *file,
411                                  const char    *display_name,
412                                  GCancellable  *cancellable,
413                                  GError       **error)
414 {
415   g_set_error_literal (error, G_IO_ERROR,
416                        G_IO_ERROR_NOT_SUPPORTED,
417                        _("Operation not supported"));
418
419   return NULL;
420 }
421
422 static GFileInputStream *
423 g_winhttp_file_read (GFile         *file,
424                      GCancellable  *cancellable,
425                      GError       **error)
426 {
427   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
428   HINTERNET connection, request;
429   const wchar_t *accept_types[] =
430     {
431       L"*/*",
432       NULL,
433     };
434
435   connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpConnect
436     (G_WINHTTP_VFS (winhttp_file->vfs)->session,
437      winhttp_file->url.lpszHostName,
438      winhttp_file->url.nPort,
439      0);
440
441   if (connection == NULL)
442     {
443       char *emsg = _g_winhttp_error_message (GetLastError ());
444
445       g_set_error (error, G_IO_ERROR,
446                    G_IO_ERROR_FAILED,
447                    "%s", emsg);
448       g_free (emsg);
449
450       return NULL;
451     }
452
453   request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpOpenRequest
454     (connection,
455      L"GET",
456      winhttp_file->url.lpszUrlPath,
457      NULL,
458      WINHTTP_NO_REFERER,
459      accept_types,
460      winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
461
462   if (request == NULL)
463     {
464       char *emsg = _g_winhttp_error_message (GetLastError ());
465
466       g_set_error (error, G_IO_ERROR,
467                    G_IO_ERROR_FAILED,
468                    "%s", emsg);
469       g_free (emsg);
470
471       return NULL;
472     }
473
474   return _g_winhttp_file_input_stream_new (winhttp_file, connection, request);
475 }
476
477 static GFileOutputStream *
478 g_winhttp_file_create (GFile             *file,
479                        GFileCreateFlags   flags,
480                        GCancellable      *cancellable,
481                        GError           **error)
482 {
483   GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
484   HINTERNET connection, request;
485
486   connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpConnect
487     (G_WINHTTP_VFS (winhttp_file->vfs)->session,
488      winhttp_file->url.lpszHostName,
489      winhttp_file->url.nPort,
490      0);
491
492   if (connection == NULL)
493     {
494       char *emsg = _g_winhttp_error_message (GetLastError ());
495
496       g_set_error (error, G_IO_ERROR,
497                    G_IO_ERROR_FAILED,
498                    "%s", emsg);
499       g_free (emsg);
500
501       return NULL;
502     }
503
504   return _g_winhttp_file_output_stream_new (winhttp_file, connection);
505 }
506
507 #if 0
508
509 static GFileOutputStream *
510 g_winhttp_file_replace (GFile             *file,
511                         const char        *etag,
512                         gboolean           make_backup,
513                         GFileCreateFlags   flags,
514                         GCancellable      *cancellable,
515                         GError           **error)
516 {
517   /* FIXME: Implement */
518
519   return NULL;
520 }
521
522
523 static gboolean
524 g_winhttp_file_delete (GFile         *file,
525                        GCancellable  *cancellable,
526                        GError       **error)
527 {
528   /* FIXME: Implement */
529
530   return FALSE;
531 }
532
533 static gboolean
534 g_winhttp_file_make_directory (GFile         *file,
535                                GCancellable  *cancellable,
536                                GError       **error)
537 {
538   /* FIXME: Implement */
539
540   return FALSE;
541 }
542
543 static gboolean
544 g_winhttp_file_copy (GFile                  *source,
545                      GFile                  *destination,
546                      GFileCopyFlags          flags,
547                      GCancellable           *cancellable,
548                      GFileProgressCallback   progress_callback,
549                      gpointer                progress_callback_data,
550                      GError                **error)
551 {
552   /* Fall back to default copy?? */
553   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Copy not supported");
554
555   return FALSE;
556 }
557
558 static gboolean
559 g_winhttp_file_move (GFile                  *source,
560                      GFile                  *destination,
561                      GFileCopyFlags          flags,
562                      GCancellable           *cancellable,
563                      GFileProgressCallback   progress_callback,
564                      gpointer                progress_callback_data,
565                      GError                **error)
566 {
567   /* FIXME: Implement */
568
569   return FALSE;
570 }
571
572 #endif
573
574 static void
575 g_winhttp_file_file_iface_init (GFileIface *iface)
576 {
577   iface->dup = g_winhttp_file_dup;
578   iface->hash = g_winhttp_file_hash;
579   iface->equal = g_winhttp_file_equal;
580   iface->is_native = g_winhttp_file_is_native;
581   iface->has_uri_scheme = g_winhttp_file_has_uri_scheme;
582   iface->get_uri_scheme = g_winhttp_file_get_uri_scheme;
583   iface->get_basename = g_winhttp_file_get_basename;
584   iface->get_path = g_winhttp_file_get_path;
585   iface->get_uri = g_winhttp_file_get_uri;
586   iface->get_parse_name = g_winhttp_file_get_parse_name;
587   iface->get_parent = g_winhttp_file_get_parent;
588   iface->prefix_matches = g_winhttp_file_prefix_matches;
589   iface->get_relative_path = g_winhttp_file_get_relative_path;
590   iface->resolve_relative_path = g_winhttp_file_resolve_relative_path;
591   iface->get_child_for_display_name = g_winhttp_file_get_child_for_display_name;
592   iface->set_display_name = g_winhttp_file_set_display_name;
593   iface->read_fn = g_winhttp_file_read;
594   iface->create = g_winhttp_file_create;
595 #if 0
596   iface->replace = g_winhttp_file_replace;
597   iface->delete_file = g_winhttp_file_delete;
598   iface->make_directory = g_winhttp_file_make_directory;
599   iface->copy = g_winhttp_file_copy;
600   iface->move = g_winhttp_file_move;
601 #endif
602 }