static csync_vio_method_handle_t *owncloud_open(const char *durl, int flags, mode_t mode) { char *uri = NULL; char *dir = NULL; char getUrl[PATH_MAX]; int put = 0; int rc = NE_OK; #ifdef _WIN32 int gtp = 0; char tmpname[13]; #endif struct transfer_context *writeCtx = NULL; csync_stat_t statBuf; memset( getUrl, '\0', PATH_MAX ); (void) mode; /* unused on webdav server */ DEBUG_WEBDAV(( "=> open called for %s\n", durl )); uri = _cleanPath( durl ); if( ! uri ) { DEBUG_WEBDAV(("Failed to clean path for %s\n", durl )); errno = EACCES; rc = NE_ERROR; } if( rc == NE_OK ) dav_connect( durl ); if (flags & O_WRONLY) { put = 1; } if (flags & O_RDWR) { put = 1; } if (flags & O_CREAT) { put = 1; } if( rc == NE_OK && put ) { /* check if the dir name exists. Otherwise return ENOENT */ dir = c_dirname( durl ); if (dir == NULL) { errno = ENOMEM; return NULL; } DEBUG_WEBDAV(("Stating directory %s\n", dir )); if( c_streq( dir, _lastDir )) { DEBUG_WEBDAV(("Dir %s is there, we know it already.\n", dir)); } else { if( owncloud_stat( dir, (csync_vio_method_handle_t*)(&statBuf) ) == 0 ) { DEBUG_WEBDAV(("Directory of file to open exists.\n")); SAFE_FREE( _lastDir ); _lastDir = c_strdup(dir); } else { DEBUG_WEBDAV(("Directory %s of file to open does NOT exist.\n", dir )); /* the directory does not exist. That is an ENOENT */ errno = ENOENT; SAFE_FREE( dir ); return NULL; } } } writeCtx = c_malloc( sizeof(struct transfer_context) ); writeCtx->bytes_written = 0; if( rc == NE_OK ) { /* open a temp file to store the incoming data */ #ifdef _WIN32 memset( tmpname, '\0', 13 ); gtp = GetTempPath( PATH_MAX, getUrl ); DEBUG_WEBDAV(("win32 tmp path: %s\n", getUrl )); if ( gtp > MAX_PATH || (gtp == 0) ) { DEBUG_WEBDAV(("Failed to compute Win32 tmp path, trying /tmp\n")); strcpy( getUrl, "/tmp/"); } strcpy( tmpname, "csync.XXXXXX" ); if( c_tmpname( tmpname ) == 0 ) { _fmode = _O_BINARY; strcat( getUrl, tmpname ); writeCtx->tmpFileName = c_strdup( getUrl ); writeCtx->fd = open( writeCtx->tmpFileName, O_RDWR | O_CREAT | O_EXCL, 0600 ); } else { writeCtx->fd = -1; } #else writeCtx->tmpFileName = c_strdup( "/tmp/csync.XXXXXX" ); writeCtx->fd = mkstemp( writeCtx->tmpFileName ); #endif DEBUG_WEBDAV(("opening temp directory %s: %d\n", writeCtx->tmpFileName, writeCtx->fd )); if( writeCtx->fd == -1 ) { DEBUG_WEBDAV(("Failed to open temp file, errno = %d\n", errno )); rc = NE_ERROR; /* errno is set by the mkstemp call above. */ } } if( rc == NE_OK && put) { DEBUG_WEBDAV(("PUT request on %s!\n", uri)); /* reset the write buffer */ writeCtx->bytes_written = 0; writeCtx->fileWritten = 0; /* flag to indicate if contents was pushed to file */ writeCtx->req = ne_request_create(dav_session.ctx, "PUT", uri); writeCtx->method = "PUT"; } if( rc == NE_OK && ! put ) { writeCtx->req = 0; writeCtx->method = "GET"; /* Download the data into a local temp file. */ /* the download via the get function requires a full uri */ snprintf( getUrl, PATH_MAX, "%s://%s%s", ne_get_scheme( dav_session.ctx), ne_get_server_hostport( dav_session.ctx ), uri ); DEBUG_WEBDAV(("GET request on %s\n", getUrl )); #define WITH_HTTP_COMPRESSION #ifdef WITH_HTTP_COMPRESSION writeCtx->req = ne_request_create( dav_session.ctx, "GET", getUrl ); /* Allow compressed content by setting the header */ ne_add_request_header( writeCtx->req, "Accept-Encoding", "gzip,deflate" ); /* hook called before the content is parsed to set the correct reader, * either the compressed- or uncompressed reader. */ ne_hook_post_headers( dav_session.ctx, install_content_reader, writeCtx ); /* actually do the request */ rc = ne_request_dispatch(writeCtx->req ); /* possible return codes are: * NE_OK, NE_AUTH, NE_CONNECT, NE_TIMEOUT, NE_ERROR (from ne_request.h) */ if( rc != NE_OK || (rc == NE_OK && ne_get_status(writeCtx->req)->klass != 2) ) { DEBUG_WEBDAV(("request_dispatch failed with rc=%d\n", rc )); if( rc == NE_OK ) rc = NE_ERROR; errno = EACCES; } /* delete the hook again, otherwise they get chained as they are with the session */ ne_unhook_post_headers( dav_session.ctx, install_content_reader, writeCtx ); /* if the compression handle is set through the post_header hook, delete it. */ if( writeCtx->decompress ) { ne_decompress_destroy( writeCtx->decompress ); } /* delete the request in any case */ ne_request_destroy(writeCtx->req); #else DEBUG_WEBDAV(("GET Compression not supported!\n")); rc = ne_get( dav_session.ctx, getUrl, writeCtx->fd ); /* FIX_ESCAPE? */ #endif if( rc != NE_OK ) { DEBUG_WEBDAV(("Download to local file failed: %d.\n", rc)); errno = EACCES; } if( close( writeCtx->fd ) == -1 ) { DEBUG_WEBDAV(("Close of local download file failed.\n")); writeCtx->fd = -1; rc = NE_ERROR; errno = EACCES; } writeCtx->fd = -1; } if( rc != NE_OK ) { SAFE_FREE( writeCtx ); } SAFE_FREE( uri ); SAFE_FREE( dir ); return (csync_vio_method_handle_t *) writeCtx; }
/* Gets a file from the owncloud url to the open file descriptor. */ static int owncloud_get(csync_vio_method_handle_t *flocal, csync_vio_method_handle_t *fremote, csync_vio_file_stat_t *vfs) { int rc = 0; int neon_stat; const ne_status *status; int fd; struct transfer_context *write_ctx = (struct transfer_context*) fremote; (void) vfs; /* stat information of the source file */ fd = csync_vio_getfd(flocal); if (fd == -1) { errno = EINVAL; return -1; } /* GET a file to the open file descriptor */ if( write_ctx == NULL ) { errno = EINVAL; return -1; } if( write_ctx->req == NULL ) { errno = EINVAL; return -1; } DEBUG_WEBDAV(" -- GET on %s", write_ctx->url); write_ctx->fd = fd; /* Allow compressed content by setting the header */ ne_add_request_header( write_ctx->req, "Accept-Encoding", "gzip,deflate" ); /* hook called before the content is parsed to set the correct reader, * either the compressed- or uncompressed reader. */ ne_hook_post_headers( dav_session.ctx, install_content_reader, write_ctx ); neon_stat = ne_request_dispatch(write_ctx->req ); /* possible return codes are: * NE_OK, NE_AUTH, NE_CONNECT, NE_TIMEOUT, NE_ERROR (from ne_request.h) */ if( neon_stat != NE_OK ) { set_errno_from_neon_errcode(neon_stat); DEBUG_WEBDAV("Error GET: Neon: %d, errno %d", neon_stat, errno); rc = -1; } else { status = ne_get_status( write_ctx->req ); if( status->klass != 2 ) { DEBUG_WEBDAV("sendfile request failed with http status %d!", status->code); set_errno_from_http_errcode( status->code ); /* decide if soft error or hard error that stops the whole sync. */ /* Currently all problems concerning one file are soft errors */ if( status->klass == 4 /* Forbidden and stuff, soft error */ ) { rc = 1; } else if( status->klass == 5 /* Server errors and such */ ) { rc = 1; /* No Abort on individual file errors. */ } else { rc = 1; } } else { DEBUG_WEBDAV("http request all cool, result code %d (%s)", status->code, status->reason_phrase ? status->reason_phrase : "<empty>"); } } /* delete the hook again, otherwise they get chained as they are with the session */ ne_unhook_post_headers( dav_session.ctx, install_content_reader, write_ctx ); /* if the compression handle is set through the post_header hook, delete it. */ if( write_ctx->decompress ) { ne_decompress_destroy( write_ctx->decompress ); } return rc; }
/* * Connect to a DAV server * This function sets the flag _connected if the connection is established * and returns if the flag is set, so calling it frequently is save. */ static int dav_connect(const char *base_url) { int useSSL = 0; int rc; char protocol[6] = {'\0'}; char uaBuf[256]; char *path = NULL; char *scheme = NULL; char *host = NULL; unsigned int port = 0; int proxystate = -1; if (_connected) { return 0; } rc = c_parse_uri( base_url, &scheme, &dav_session.user, &dav_session.pwd, &host, &port, &path ); if( rc < 0 ) { DEBUG_WEBDAV("Failed to parse uri %s", base_url ); goto out; } DEBUG_WEBDAV("* scheme %s", scheme ); DEBUG_WEBDAV("* host %s", host ); DEBUG_WEBDAV("* port %u", port ); DEBUG_WEBDAV("* path %s", path ); if( strcmp( scheme, "owncloud" ) == 0 ) { strcpy( protocol, "http"); } else if( strcmp( scheme, "ownclouds" ) == 0 ) { strcpy( protocol, "https"); useSSL = 1; } else { DEBUG_WEBDAV("Invalid scheme %s, go outa here!", scheme ); rc = -1; goto out; } DEBUG_WEBDAV("* user %s", dav_session.user ? dav_session.user : ""); if (port == 0) { port = ne_uri_defaultport(protocol); } #if 0 rc = ne_sock_init(); DEBUG_WEBDAV("ne_sock_init: %d", rc ); if (rc < 0) { rc = -1; goto out; } #endif dav_session.ctx = ne_session_create( protocol, host, port); if (dav_session.ctx == NULL) { DEBUG_WEBDAV("Session create with protocol %s failed", protocol ); rc = -1; goto out; } if (dav_session.read_timeout == 0) dav_session.read_timeout = 300; // set 300 seconds as default. ne_set_read_timeout(dav_session.ctx, dav_session.read_timeout); snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) csyncoC/%s", get_platform(), CSYNC_STRINGIFY( LIBCSYNC_VERSION )); ne_set_useragent( dav_session.ctx, uaBuf); ne_set_server_auth(dav_session.ctx, ne_auth, 0 ); if( useSSL ) { if (!ne_has_support(NE_FEATURE_SSL)) { DEBUG_WEBDAV("Error: SSL is not enabled."); rc = -1; goto out; } ne_ssl_trust_default_ca( dav_session.ctx ); ne_ssl_set_verify( dav_session.ctx, verify_sslcert, 0 ); } /* Hook called when a request is created. It sets the proxy connection header. */ ne_hook_create_request( dav_session.ctx, request_created_hook, NULL ); /* Hook called after response headers are read. It gets the Session ID. */ ne_hook_post_headers( dav_session.ctx, post_request_hook, NULL ); /* Hook called before a request is sent. It sets the cookies. */ ne_hook_pre_send( dav_session.ctx, pre_send_hook, NULL ); /* Hook called after request is dispatched. Used for handling possible redirections. */ ne_hook_post_send( dav_session.ctx, post_send_hook, NULL ); /* Proxy support */ proxystate = configureProxy( dav_session.ctx ); if( proxystate < 0 ) { DEBUG_WEBDAV("Error: Proxy-Configuration failed."); } else if( proxystate > 0 ) { ne_set_proxy_auth( dav_session.ctx, ne_proxy_auth, 0 ); } _connected = 1; rc = 0; out: SAFE_FREE(path); SAFE_FREE(host); SAFE_FREE(scheme); return rc; }