soup-directory-input-stream: return a titleless html
[platform/upstream/libsoup.git] / libsoup / soup-directory-input-stream.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2008, 2010 Red Hat, Inc.
4  * Copyright (C) 2010 Igalia, S.L.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "soup-directory-input-stream.h"
27
28 #include <libsoup/soup.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define INIT_STRING "<html>\n<body>\n<table><th align=\"left\">Name</th><th>Size</th><th>Date Modified</th>\n"
33 #define ROW_FORMAT  "<td><a href=\"%s\">%s</a></td><td align=\"right\">%s</td><td align=\"right\" margin=8>%s</td>\n"
34 #define EXIT_STRING "</table>\n</html>\n"
35
36 G_DEFINE_TYPE (SoupDirectoryInputStream, soup_directory_input_stream, G_TYPE_INPUT_STREAM)
37
38 static SoupBuffer *
39 soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream,
40                                         GFileInfo *info)
41 {
42         SoupBuffer *buffer;
43         GString *string;
44         const char *file_name;
45         char *escaped, *path, *xml_string, *size, *time;
46         GTimeVal modified;
47         GDateTime *modification_time;
48
49         if (!g_file_info_get_name (info))
50                 return NULL;
51
52         file_name = g_file_info_get_display_name (info);
53         if (!file_name) {
54                 file_name = g_file_info_get_name (info);
55                 /* FIXME: convert somehow? */
56                 if (!g_utf8_validate (file_name, -1, NULL))
57                         return NULL;
58         }
59         string = g_string_new ("<tr>");
60
61         xml_string = g_markup_escape_text (file_name, -1);
62         escaped = g_uri_escape_string (file_name, NULL, FALSE);
63         path = g_strconcat (stream->uri, G_DIR_SEPARATOR_S, escaped, NULL);
64         size = g_format_size_for_display (g_file_info_get_size (info));
65         g_file_info_get_modification_time (info, &modified);
66         modification_time = g_date_time_new_from_timeval_local (&modified);
67         time = g_date_time_format (modification_time, "%X %x");
68         g_date_time_unref (modification_time);
69
70         g_string_append_printf (string, ROW_FORMAT, path, xml_string, size, time);
71         g_string_append (string, "</tr>\n");
72         buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
73
74         g_free (time);
75         g_free (escaped);
76         g_free (size);
77         g_free (path);
78         g_free (xml_string);
79         g_string_free (string, FALSE);
80
81         return buffer;
82 }
83
84 static SoupBuffer *
85 soup_directory_input_stream_read_next_file (SoupDirectoryInputStream  *stream,
86                                             GCancellable              *cancellable,
87                                             GError                   **error)
88 {
89         GFileInfo *info;
90         SoupBuffer *buffer;
91         GError *err = NULL;
92
93         do {
94                 info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err);
95                 if (info == NULL) {
96                         if (err) {
97                                 g_propagate_error (error, err);
98                                 return NULL;
99                         } else if (!stream->done) {
100                                 stream->done = TRUE;
101                                 return soup_buffer_new (SOUP_MEMORY_STATIC,
102                                                         EXIT_STRING,
103                                                         sizeof (EXIT_STRING));
104                         } else {
105                                 return NULL;
106                         }
107                 }
108
109                 buffer = soup_directory_input_stream_parse_info (stream, info);
110                 g_object_unref (info);
111         } while (buffer == NULL);
112
113         return buffer;
114 }
115
116 static gssize
117 soup_directory_input_stream_read (GInputStream  *input,
118                                   void          *buffer,
119                                   gsize          count,
120                                   GCancellable  *cancellable,
121                                   GError       **error)
122 {
123         SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
124         gsize total, size;
125
126         for (total = 0; total < count; total += size) {
127                 if (stream->buffer == NULL) {
128                         stream->buffer = soup_directory_input_stream_read_next_file (stream, cancellable, error);
129                         if (stream->buffer == NULL) {
130                                 /* FIXME: Is this correct or should we forward the error? */
131                                 if (total)
132                                         g_clear_error (error);
133                                 return total;
134                         }
135                 }
136
137                 size = MIN (stream->buffer->length, count - total);
138                 memcpy ((char *)buffer + total, stream->buffer->data, size);
139                 if (size == stream->buffer->length) {
140                         soup_buffer_free (stream->buffer);
141                         stream->buffer = NULL;
142                 } else {
143                         SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer,
144                                                                      size,
145                                                                      stream->buffer->length - size);
146                         soup_buffer_free (stream->buffer);
147                         stream->buffer = sub;
148                 }
149         }
150
151         return total;
152 }
153
154 static gboolean
155 soup_directory_input_stream_close (GInputStream  *input,
156                                    GCancellable  *cancellable,
157                                    GError       **error)
158 {
159         SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
160         gboolean result;
161
162         if (stream->buffer) {
163                 soup_buffer_free (stream->buffer);
164                 stream->buffer = NULL;
165         }
166
167         result = g_file_enumerator_close (stream->enumerator,
168                                           cancellable,
169                                           error);
170         g_object_unref (stream->enumerator);
171         stream->enumerator = NULL;
172
173         g_free (stream->uri);
174         stream->uri = NULL;
175
176         return result;
177 }
178
179 static void
180 soup_directory_input_stream_class_init (SoupDirectoryInputStreamClass *stream_class)
181 {
182         GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class);
183
184         inputstream_class->read_fn = soup_directory_input_stream_read;
185         inputstream_class->close_fn = soup_directory_input_stream_close;
186 }
187
188 static void
189 soup_directory_input_stream_init (SoupDirectoryInputStream *stream)
190 {
191         stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
192                                           INIT_STRING,
193                                           sizeof (INIT_STRING));
194 }
195
196 GInputStream *
197 soup_directory_input_stream_new (GFileEnumerator *enumerator,
198                                  SoupURI         *uri)
199 {
200         GInputStream *stream;
201
202         g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
203         g_return_val_if_fail (uri != NULL, NULL);
204
205         stream = g_object_new (SOUP_TYPE_DIRECTORY_INPUT_STREAM, NULL);
206
207         SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
208         SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
209
210         return stream;
211 }
212