Search code examples
capachefile-uploadwebserverapache-modules

Uploading file to apache server in C (without web headers)


I'm developing webserver (Apache 2.4 in linux) and trying to support file upload from client side to server side. I succeed to receive the file in server side, but I'm getting an extra web headers in the uploaded file content which I want to omit. For example, I when uploading example.txt which contain:

I'm the file content!

In server side file I'm getting:

------WebKitFormBoundaryqbGGz0VOmz7CVPCF
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: application/octet-stream
I'm the file content!
------WebKitFormBoundaryqbGGz0VOmz7CVPCF--

The actual file with be binary, so it's should contain the exact content without additions data.

I used these examples: mod_upload and mod_csv.

My sever side code is:

apr_bucket_brigade* bb;
apr_bucket* b;
int status = 0;
int end = 0;
char* fname = 0;
const char* buf;
apr_size_t bytes;
char buffer[512];
apr_file_t* tmpfile;

char* tmpname = apr_pstrdup(r->pool, "/tmp/tmp-upload.XXXXXX") ;

if ( apr_file_mktemp(&tmpfile, tmpname, KEEPONCLOSE, r->pool) != APR_SUCCESS ) {
    ap_remove_input_filter(r->input_filters) ;
}

apr_pool_cleanup_register(r->pool, tmpfile, (void*)apr_file_close, apr_pool_cleanup_null) ;
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);

do {
    status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, BLOCKSIZE) ;
    if ( status == APR_SUCCESS ) {
        for (b = APR_BRIGADE_FIRST(bb) ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_NEXT(b)) {
            if (APR_BUCKET_IS_EOS(b)) {
                end = 1;
                break;
            }
            else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS) {
                apr_file_write(tmpfile, buf, &bytes);
                char* x = apr_pstrndup(r->pool, buf, bytes);
                if (fname)
                    fname = apr_pstrcat(r->pool, fname, x, NULL);
                else
                    fname = x;
            }
            else {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bucket read error") ;
            }
        }
    }
    else {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Brigade error") ;
    }
    apr_brigade_cleanup(bb);
} while ( !end && status == APR_SUCCESS );
apr_brigade_destroy(bb);

return OK;

Any ideas how to change the code to avoid the redundant headers in the result file content / any other way (/method) to get the file in the server?

Thanks!


Solution

  • So... my solution was to manually remove these prefix&suffix headers. It may help someone in the future:

    int getPrefixHeaderLength(const char* buf) {
        int rows = 0;
        int counter = 0;
        char* ret = buf;
    
        if (buf == NULL)
            return 0;
    
        while (*ret != 0 && rows < 4) {
            if (*ret == '\n')
                rows++;
    
            ret++;
            counter++;
        }
    
        return counter;
    }
    
    /* example buf: "... filename="example.txt" "  */
    void getFilename(const char* buf, char* filename)
    {
        char* pEnd = NULL;
        char *pFilename = strstr(buf,"filename");
        pFilename += 10;
        pEnd = strchr(pFilename,'"');
        snprintf(filename,pEnd-pFilename+1,"%s",pFilename);
    }
    
    apr_bucket_brigade* bb;
    apr_bucket* b;
    int status = 0;
    int end = 0;
    char* fname = 0;
    const char* buf;
    apr_size_t bytes;
    apr_size_t totalBytes = 0;
    char buffer[512];
    apr_file_t* tmpfile;
    const char* ctype = apr_table_get(r->headers_in, "file-0") ;
    int prefixFlag = 1;
    int counter = 0;
    apr_off_t offsetIndex = 0;
    char filename[512];
    
    char* tmpname = apr_pstrdup(r->pool, "/tmp/tmp-upload.XXXXXX") ;
    
    if ( apr_file_mktemp(&tmpfile, tmpname, KEEPONCLOSE, r->pool) != APR_SUCCESS ) {
        ap_remove_input_filter(r->input_filters) ;
    }
    
    apr_pool_cleanup_register(r->pool, tmpfile, (void*)apr_file_close, apr_pool_cleanup_null) ;
    
    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    
    do {
        status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, BLOCKSIZE) ;
        if ( status == APR_SUCCESS ) {
            for (b = APR_BRIGADE_FIRST(bb) ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_NEXT(b)) {
                if (APR_BUCKET_IS_EOS(b)) {
                    end = 1;
                    break;
                }
                else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS) {
                    int prefixHeaderLength = 0;
                    apr_size_t sizeToWrite;
    
                    if (prefixFlag) {
                        if (bytes == 1) {
                            continue;
                        }
    
                        prefixHeaderLength = getPrefixHeaderLength(buf);
                        prefixFlag = 0;
                        getFilename(buf,filename);
                        ap_rprintf(r, "\"filename\":\"%s\",",filename);
                    }
                    //ap_rprintf(r, "\"counter%d\":%d,",counter,bytes);
                    sizeToWrite = bytes - prefixHeaderLength;
                    apr_file_write(tmpfile, buf+prefixHeaderLength, &sizeToWrite);
                    //ap_rprintf(r, "\"write%d\":%d,",counter,sizeToWrite);
                    totalBytes += bytes;
                }
                else {
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Bucket read error") ;
                }
            }
        }
        else {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Brigade error") ;
        }
        apr_brigade_cleanup(bb);
        counter++;
    } while ( !end && status == APR_SUCCESS );
    
    counter = 0;
    
    while (counter < 2) {
        char c;
        offsetIndex = -2L;
        apr_file_seek(tmpfile,SEEK_CUR, &offsetIndex);
        apr_file_getc(&c,tmpfile);
    
        if (c == '\r')
            counter++;
    }
    
    apr_file_trunc(tmpfile,offsetIndex);
    ap_rprintf(r, "\"size\":%d}",totalBytes);
    
    apr_brigade_destroy(bb);
    return OK;