15 * this is a uploadprogress for a lighttpd plugin
22 } connection_map_entry
;
25 connection_map_entry
**ptr
;
31 /* plugin config for all request/connections */
40 connection_map
*con_map
;
42 plugin_config
**config_storage
;
53 /* init the plugin data */
54 static connection_map
*connection_map_init() {
57 cm
= calloc(1, sizeof(*cm
));
62 static void connection_map_free(connection_map
*cm
) {
64 for (i
= 0; i
< cm
->size
; i
++) {
65 connection_map_entry
*cme
= cm
->ptr
[i
];
70 buffer_free(cme
->con_id
);
78 static int connection_map_insert(connection_map
*cm
, connection
*con
, const char *con_id
, size_t idlen
) {
79 connection_map_entry
*cme
;
84 cm
->ptr
= malloc(cm
->size
* sizeof(*(cm
->ptr
)));
85 for (i
= 0; i
< cm
->size
; i
++) {
88 } else if (cm
->used
== cm
->size
) {
90 cm
->ptr
= realloc(cm
->ptr
, cm
->size
* sizeof(*(cm
->ptr
)));
91 for (i
= cm
->used
; i
< cm
->size
; i
++) {
96 if (cm
->ptr
[cm
->used
]) {
97 /* is already alloced, just reuse it */
98 cme
= cm
->ptr
[cm
->used
];
100 cme
= malloc(sizeof(*cme
));
101 cme
->con_id
= buffer_init();
103 buffer_copy_string_len(cme
->con_id
, con_id
, idlen
);
106 cm
->ptr
[cm
->used
++] = cme
;
111 static connection
*connection_map_get_connection(connection_map
*cm
, const char *con_id
, size_t idlen
) {
114 for (i
= 0; i
< cm
->used
; i
++) {
115 connection_map_entry
*cme
= cm
->ptr
[i
];
117 if (buffer_is_equal_string(cme
->con_id
, con_id
, idlen
)) {
118 /* found connection */
126 static int connection_map_remove_connection(connection_map
*cm
, connection
*con
) {
129 for (i
= 0; i
< cm
->used
; i
++) {
130 connection_map_entry
*cme
= cm
->ptr
[i
];
132 if (cme
->con
== con
) {
133 /* found connection */
135 buffer_reset(cme
->con_id
);
140 /* swap positions with the last entry */
142 cm
->ptr
[i
] = cm
->ptr
[cm
->used
];
143 cm
->ptr
[cm
->used
] = cme
;
153 /* init the plugin data */
154 INIT_FUNC(mod_uploadprogress_init
) {
157 p
= calloc(1, sizeof(*p
));
159 p
->con_map
= connection_map_init();
164 /* detroy the plugin data */
165 FREE_FUNC(mod_uploadprogress_free
) {
166 plugin_data
*p
= p_d
;
170 if (!p
) return HANDLER_GO_ON
;
172 if (p
->config_storage
) {
174 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
175 plugin_config
*s
= p
->config_storage
[i
];
177 if (NULL
== s
) continue;
179 buffer_free(s
->progress_url
);
183 free(p
->config_storage
);
186 connection_map_free(p
->con_map
);
190 return HANDLER_GO_ON
;
193 /* handle plugin config and check values */
195 SETDEFAULTS_FUNC(mod_uploadprogress_set_defaults
) {
196 plugin_data
*p
= p_d
;
199 config_values_t cv
[] = {
200 { "upload-progress.progress-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
201 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
204 if (!p
) return HANDLER_ERROR
;
206 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
208 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
209 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
212 s
= calloc(1, sizeof(plugin_config
));
213 s
->progress_url
= buffer_init();
215 cv
[0].destination
= s
->progress_url
;
217 p
->config_storage
[i
] = s
;
219 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
220 return HANDLER_ERROR
;
224 return HANDLER_GO_ON
;
229 static int mod_uploadprogress_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
231 plugin_config
*s
= p
->config_storage
[0];
235 /* skip the first, the global context */
236 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
237 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
238 s
= p
->config_storage
[i
];
240 /* condition didn't match */
241 if (!config_check_cond(srv
, con
, dc
)) continue;
244 for (j
= 0; j
< dc
->value
->used
; j
++) {
245 data_unset
*du
= dc
->value
->data
[j
];
247 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("upload-progress.progress-url"))) {
261 * for the first request we check if it is a post-request
263 * if no, move out, don't care about them
265 * if yes, take the connection structure and register it locally
266 * in the progress-struct together with an session-id (md5 ... )
268 * if the connections closes, cleanup the entry in the progress-struct
270 * a second request can now get the info about the size of the upload,
275 URIHANDLER_FUNC(mod_uploadprogress_uri_handler
) {
276 plugin_data
*p
= p_d
;
281 connection
*post_con
= NULL
;
284 if (buffer_string_is_empty(con
->uri
.path
)) return HANDLER_GO_ON
;
285 switch(con
->request
.http_method
) {
286 case HTTP_METHOD_GET
:
287 case HTTP_METHOD_POST
: break;
288 default: return HANDLER_GO_ON
;
291 mod_uploadprogress_patch_connection(srv
, con
, p
);
292 if (buffer_string_is_empty(p
->conf
.progress_url
)) return HANDLER_GO_ON
;
294 if (con
->request
.http_method
== HTTP_METHOD_GET
) {
295 if (!buffer_is_equal(con
->uri
.path
, p
->conf
.progress_url
)) {
296 return HANDLER_GO_ON
;
300 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "X-Progress-ID"))) {
302 } else if (!buffer_string_is_empty(con
->uri
.query
)
303 && (id
= strstr(con
->uri
.query
->ptr
, "X-Progress-ID="))) {
304 /* perhaps the POST request is using the query-string to pass the X-Progress-ID */
305 id
+= sizeof("X-Progress-ID=")-1;
307 /*(path-info is not known at this point in request)*/
308 id
= con
->uri
.path
->ptr
;
309 len
= buffer_string_length(con
->uri
.path
);
310 if (len
>= 33 && id
[len
-33] == '/') {
314 return HANDLER_GO_ON
;
318 /* the request has to contain a 32byte ID */
319 for (len
= 0; light_isxdigit(id
[len
]); ++len
) ;
321 if (!pathinfo
) { /*(reduce false positive noise in error log)*/
322 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
323 "invalid progress-id; non-xdigit or len != 32:", id
);
325 return HANDLER_GO_ON
;
328 /* check if this is a POST request */
329 switch(con
->request
.http_method
) {
330 case HTTP_METHOD_POST
:
332 connection_map_insert(p
->con_map
, con
, id
, len
);
334 return HANDLER_GO_ON
;
335 case HTTP_METHOD_GET
:
336 buffer_reset(con
->physical
.path
);
338 con
->file_started
= 1;
339 con
->file_finished
= 1;
341 con
->http_status
= 200;
344 /* get the connection */
345 if (NULL
== (post_con
= connection_map_get_connection(p
->con_map
, id
, len
))) {
346 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
347 "ID not known:", id
);
349 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("not in progress"));
351 return HANDLER_FINISHED
;
354 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml"));
356 /* just an attempt the force the IE/proxies to NOT cache the request ... doesn't help :( */
357 response_header_overwrite(srv
, con
, CONST_STR_LEN("Pragma"), CONST_STR_LEN("no-cache"));
358 response_header_overwrite(srv
, con
, CONST_STR_LEN("Expires"), CONST_STR_LEN("Thu, 19 Nov 1981 08:52:00 GMT"));
359 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"));
364 buffer_copy_string_len(b
, CONST_STR_LEN(
365 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
368 buffer_append_int(b
, post_con
->request
.content_length
);
369 buffer_append_string_len(b
, CONST_STR_LEN(
372 buffer_append_int(b
, post_con
->request_content_queue
->bytes_in
);
373 buffer_append_string_len(b
, CONST_STR_LEN(
378 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "...", b
);
381 chunkqueue_append_buffer(con
->write_queue
, b
);
384 return HANDLER_FINISHED
;
389 return HANDLER_GO_ON
;
392 REQUESTDONE_FUNC(mod_uploadprogress_request_done
) {
393 plugin_data
*p
= p_d
;
397 if (con
->request
.http_method
!= HTTP_METHOD_POST
) return HANDLER_GO_ON
;
398 if (buffer_string_is_empty(con
->uri
.path
)) return HANDLER_GO_ON
;
400 if (connection_map_remove_connection(p
->con_map
, con
)) {
404 return HANDLER_GO_ON
;
407 /* this function is called at dlopen() time and inits the callbacks */
409 int mod_uploadprogress_plugin_init(plugin
*p
);
410 int mod_uploadprogress_plugin_init(plugin
*p
) {
411 p
->version
= LIGHTTPD_VERSION_ID
;
412 p
->name
= buffer_init_string("uploadprogress");
414 p
->init
= mod_uploadprogress_init
;
415 p
->handle_uri_clean
= mod_uploadprogress_uri_handler
;
416 p
->connection_reset
= mod_uploadprogress_request_done
;
417 p
->set_defaults
= mod_uploadprogress_set_defaults
;
418 p
->cleanup
= mod_uploadprogress_free
;