10 #include "stat_cache.h"
17 * this is a uploadprogress for a lighttpd plugin
24 } connection_map_entry
;
27 connection_map_entry
**ptr
;
33 /* plugin config for all request/connections */
42 connection_map
*con_map
;
44 plugin_config
**config_storage
;
55 /* init the plugin data */
56 static connection_map
*connection_map_init() {
59 cm
= calloc(1, sizeof(*cm
));
64 static void connection_map_free(connection_map
*cm
) {
66 for (i
= 0; i
< cm
->size
; i
++) {
67 connection_map_entry
*cme
= cm
->ptr
[i
];
72 buffer_free(cme
->con_id
);
80 static int connection_map_insert(connection_map
*cm
, connection
*con
, buffer
*con_id
) {
81 connection_map_entry
*cme
;
86 cm
->ptr
= malloc(cm
->size
* sizeof(*(cm
->ptr
)));
87 for (i
= 0; i
< cm
->size
; i
++) {
90 } else if (cm
->used
== cm
->size
) {
92 cm
->ptr
= realloc(cm
->ptr
, cm
->size
* sizeof(*(cm
->ptr
)));
93 for (i
= cm
->used
; i
< cm
->size
; i
++) {
98 if (cm
->ptr
[cm
->used
]) {
99 /* is already alloced, just reuse it */
100 cme
= cm
->ptr
[cm
->used
];
102 cme
= malloc(sizeof(*cme
));
104 cme
->con_id
= buffer_init();
105 buffer_copy_buffer(cme
->con_id
, con_id
);
108 cm
->ptr
[cm
->used
++] = cme
;
113 static connection
*connection_map_get_connection(connection_map
*cm
, buffer
*con_id
) {
116 for (i
= 0; i
< cm
->used
; i
++) {
117 connection_map_entry
*cme
= cm
->ptr
[i
];
119 if (buffer_is_equal(cme
->con_id
, con_id
)) {
120 /* found connection */
128 static int connection_map_remove_connection(connection_map
*cm
, connection
*con
) {
131 for (i
= 0; i
< cm
->used
; i
++) {
132 connection_map_entry
*cme
= cm
->ptr
[i
];
134 if (cme
->con
== con
) {
135 /* found connection */
137 buffer_reset(cme
->con_id
);
142 /* swap positions with the last entry */
144 cm
->ptr
[i
] = cm
->ptr
[cm
->used
];
145 cm
->ptr
[cm
->used
] = cme
;
155 /* init the plugin data */
156 INIT_FUNC(mod_uploadprogress_init
) {
159 p
= calloc(1, sizeof(*p
));
161 p
->con_map
= connection_map_init();
166 /* detroy the plugin data */
167 FREE_FUNC(mod_uploadprogress_free
) {
168 plugin_data
*p
= p_d
;
172 if (!p
) return HANDLER_GO_ON
;
174 if (p
->config_storage
) {
176 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
177 plugin_config
*s
= p
->config_storage
[i
];
179 if (NULL
== s
) continue;
181 buffer_free(s
->progress_url
);
185 free(p
->config_storage
);
188 connection_map_free(p
->con_map
);
192 return HANDLER_GO_ON
;
195 /* handle plugin config and check values */
197 SETDEFAULTS_FUNC(mod_uploadprogress_set_defaults
) {
198 plugin_data
*p
= p_d
;
201 config_values_t cv
[] = {
202 { "upload-progress.progress-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
203 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
206 if (!p
) return HANDLER_ERROR
;
208 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
210 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
211 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
214 s
= calloc(1, sizeof(plugin_config
));
215 s
->progress_url
= buffer_init();
217 cv
[0].destination
= s
->progress_url
;
219 p
->config_storage
[i
] = s
;
221 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
222 return HANDLER_ERROR
;
226 return HANDLER_GO_ON
;
231 static int mod_uploadprogress_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
233 plugin_config
*s
= p
->config_storage
[0];
237 /* skip the first, the global context */
238 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
239 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
240 s
= p
->config_storage
[i
];
242 /* condition didn't match */
243 if (!config_check_cond(srv
, con
, dc
)) continue;
246 for (j
= 0; j
< dc
->value
->used
; j
++) {
247 data_unset
*du
= dc
->value
->data
[j
];
249 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("upload-progress.progress-url"))) {
263 * for the first request we check if it is a post-request
265 * if no, move out, don't care about them
267 * if yes, take the connection structure and register it locally
268 * in the progress-struct together with an session-id (md5 ... )
270 * if the connections closes, cleanup the entry in the progress-struct
272 * a second request can now get the info about the size of the upload,
277 URIHANDLER_FUNC(mod_uploadprogress_uri_handler
) {
278 plugin_data
*p
= p_d
;
282 connection
*post_con
= NULL
;
286 if (con
->uri
.path
->used
== 0) return HANDLER_GO_ON
;
288 mod_uploadprogress_patch_connection(srv
, con
, p
);
290 /* check if this is a POST request */
291 switch(con
->request
.http_method
) {
292 case HTTP_METHOD_POST
:
293 /* the request has to contain a 32byte ID */
295 if (NULL
== (ds
= (data_string
*)array_get_element(con
->request
.headers
, "X-Progress-ID"))) {
296 if (!buffer_string_is_empty(con
->uri
.query
)) {
297 /* perhaps the POST request is using the querystring to pass the X-Progress-ID */
300 return HANDLER_GO_ON
;
306 if (b
->used
!= 32 + 1) {
307 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
308 "len of progress-id != 32:", b
->used
- 1);
309 return HANDLER_GO_ON
;
312 for (i
= 0; i
< b
->used
- 1; i
++) {
315 if (!light_isxdigit(c
)) {
316 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
317 "non-xdigit in progress-id:", b
);
318 return HANDLER_GO_ON
;
322 connection_map_insert(p
->con_map
, con
, b
);
324 return HANDLER_GO_ON
;
325 case HTTP_METHOD_GET
:
326 if (!buffer_is_equal(con
->uri
.path
, p
->conf
.progress_url
)) {
327 return HANDLER_GO_ON
;
330 if (NULL
== (ds
= (data_string
*)array_get_element(con
->request
.headers
, "X-Progress-ID"))) {
331 if (!buffer_string_is_empty(con
->uri
.query
)) {
332 /* perhaps the GET request is using the querystring to pass the X-Progress-ID */
335 return HANDLER_GO_ON
;
341 if (b
->used
!= 32 + 1) {
342 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
343 "len of progress-id != 32:", b
->used
- 1);
344 return HANDLER_GO_ON
;
347 for (i
= 0; i
< b
->used
- 1; i
++) {
350 if (!light_isxdigit(c
)) {
351 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
352 "non-xdigit in progress-id:", b
);
353 return HANDLER_GO_ON
;
357 buffer_reset(con
->physical
.path
);
359 con
->file_started
= 1;
360 con
->file_finished
= 1;
362 con
->http_status
= 200;
365 /* get the connection */
366 if (NULL
== (post_con
= connection_map_get_connection(p
->con_map
, b
))) {
367 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
370 chunkqueue_get_append_mem(con
->write_queue
, CONST_STR_LEN("starting"));
372 return HANDLER_FINISHED
;
375 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml"));
377 /* just an attempt the force the IE/proxies to NOT cache the request ... doesn't help :( */
378 response_header_overwrite(srv
, con
, CONST_STR_LEN("Pragma"), CONST_STR_LEN("no-cache"));
379 response_header_overwrite(srv
, con
, CONST_STR_LEN("Expires"), CONST_STR_LEN("Thu, 19 Nov 1981 08:52:00 GMT"));
380 response_header_overwrite(srv
, con
, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("no-store, no-cache, must-revalidate, post-check=0, pre-check=0"));
385 buffer_copy_string_len(b
, CONST_STR_LEN(
386 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
389 buffer_append_int(b
, post_con
->request
.content_length
);
390 buffer_append_string_len(b
, CONST_STR_LEN(
393 buffer_append_int(b
, post_con
->request_content_queue
->bytes_in
);
394 buffer_append_string_len(b
, CONST_STR_LEN(
399 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "...", b
);
402 chunkqueue_append_buffer(con
->write_queue
, b
);
405 return HANDLER_FINISHED
;
410 return HANDLER_GO_ON
;
413 REQUESTDONE_FUNC(mod_uploadprogress_request_done
) {
414 plugin_data
*p
= p_d
;
418 if (con
->uri
.path
->used
== 0) return HANDLER_GO_ON
;
420 if (connection_map_remove_connection(p
->con_map
, con
)) {
424 return HANDLER_GO_ON
;
427 /* this function is called at dlopen() time and inits the callbacks */
429 int mod_uploadprogress_plugin_init(plugin
*p
);
430 int mod_uploadprogress_plugin_init(plugin
*p
) {
431 p
->version
= LIGHTTPD_VERSION_ID
;
432 p
->name
= buffer_init_string("uploadprogress");
434 p
->init
= mod_uploadprogress_init
;
435 p
->handle_uri_clean
= mod_uploadprogress_uri_handler
;
436 p
->handle_request_done
= mod_uploadprogress_request_done
;
437 p
->set_defaults
= mod_uploadprogress_set_defaults
;
438 p
->cleanup
= mod_uploadprogress_free
;