1 /**
2  * @copyright
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  * @endcopyright
22  *
23  * @file org_apache_subversion_javahl_util_TunnelChannel.cpp
24  * @brief Implementation of the native methods in the Java classes
25  *        TunnelChannel, RequestChannel and ResponseChannel
26  */
27 
28 #include <string>
29 
30 #include <apr_file_io.h>
31 
32 #include "../include/org_apache_subversion_javahl_util_TunnelChannel.h"
33 #include "../include/org_apache_subversion_javahl_util_RequestChannel.h"
34 #include "../include/org_apache_subversion_javahl_util_ResponseChannel.h"
35 
36 #include "jniwrapper/jni_exception.hpp"
37 #include "jniwrapper/jni_channel.hpp"
38 #include "jniwrapper/jni_stack.hpp"
39 
40 #include "svn_private_config.h"
41 
42 namespace {
get_file_descriptor(Java::Env env,jlong jfd)43 apr_file_t* get_file_descriptor(Java::Env env, jlong jfd)
44 {
45   apr_file_t* fd = reinterpret_cast<apr_file_t*>(jfd);
46   if (!fd)
47     Java::NullPointerException(env).raise("nativeChannel");
48   return fd;
49 }
50 
throw_IOException(Java::Env env,const char * message,apr_status_t status)51 void throw_IOException(Java::Env env, const char* message,
52                        apr_status_t status)
53 {
54   char buf[1024];
55   std::string msg(message);
56   apr_strerror(status, buf, sizeof(buf) - 1);
57   msg += buf;
58   Java::IOException(env).raise(msg.c_str());
59 }
60 
61 class TunnelReader : public Java::ChannelReader
62 {
63 public:
TunnelReader(Java::Env env,jlong jnative_channel)64   explicit TunnelReader(Java::Env env, jlong jnative_channel)
65     : m_fd(get_file_descriptor(env, jnative_channel))
66     {}
67 
operator ()(Java::Env env,void * buffer,jint length)68   virtual jint operator()(Java::Env env, void* buffer, jint length)
69     {
70       if (!length)
71         return 0;
72 
73       apr_size_t bytes_read = length;
74       const apr_status_t status = apr_file_read(m_fd, buffer, &bytes_read);
75       if (status && !APR_STATUS_IS_EOF(status))
76         {
77           throw_IOException(
78               env, _("Error reading from native file handle: "),
79               status);
80           return -1;
81         }
82       if (APR_STATUS_IS_EOF(status))
83         return -1;
84       return jint(bytes_read);
85     }
86 
87 private:
88   apr_file_t* const m_fd;
89 };
90 
91 class TunnelWriter : public Java::ChannelWriter
92 {
93 public:
TunnelWriter(Java::Env env,jlong jnative_channel)94   explicit TunnelWriter(Java::Env env, jlong jnative_channel)
95     : m_fd(get_file_descriptor(env, jnative_channel))
96     {}
97 
operator ()(Java::Env env,const void * buffer,jint length)98   virtual jint operator()(Java::Env env, const void* buffer, jint length)
99     {
100       if (!length)
101         return 0;
102 
103       apr_size_t bytes_written;
104       const apr_status_t status =
105         apr_file_write_full(m_fd, buffer, length, &bytes_written);
106       if (status)
107         {
108           throw_IOException(
109               env, _("Error writing to native file handle: "),
110               status);
111           return -1;
112         }
113       return jint(bytes_written);
114     }
115 
116 private:
117   apr_file_t* const m_fd;
118 };
119 
120 } // anonymous namespace
121 
122 
123 JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_util_TunnelChannel_nativeClose(JNIEnv * jenv,jclass jclazz,jlong jnative_channel)124 Java_org_apache_subversion_javahl_util_TunnelChannel_nativeClose(
125     JNIEnv* jenv, jclass jclazz, jlong jnative_channel)
126 {
127   SVN_JAVAHL_JNI_TRY_STATIC(TunnelChannel, close)
128     {
129       const Java::Env env(jenv);
130 
131       apr_file_t* const fd = get_file_descriptor(env, jnative_channel);
132       if (!fd)
133         return;
134 
135       const apr_status_t status = apr_file_close(fd);
136       if (status)
137         throw_IOException(
138             env, _("Error closing native file handle: "),
139             status);
140     }
141   SVN_JAVAHL_JNI_CATCH;
142 }
143 
144 JNIEXPORT jint JNICALL
Java_org_apache_subversion_javahl_util_RequestChannel_nativeRead(JNIEnv * jenv,jclass jclazz,jlong jnative_channel,jobject jdst_buffer)145 Java_org_apache_subversion_javahl_util_RequestChannel_nativeRead(
146     JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jdst_buffer)
147 {
148   SVN_JAVAHL_JNI_TRY_STATIC(RequestChannel, read)
149     {
150       const Java::Env env(jenv);
151 
152       TunnelReader reader(env, jnative_channel);
153       Java::ReadableByteChannel channel(env, reader);
154       return channel.read(jdst_buffer);
155     }
156   SVN_JAVAHL_JNI_CATCH;
157   return -1;
158 }
159 
160 JNIEXPORT jint JNICALL
Java_org_apache_subversion_javahl_util_ResponseChannel_nativeWrite(JNIEnv * jenv,jclass jclazz,jlong jnative_channel,jobject jsrc_buffer)161 Java_org_apache_subversion_javahl_util_ResponseChannel_nativeWrite(
162     JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jsrc_buffer)
163 {
164   SVN_JAVAHL_JNI_TRY_STATIC(ResponseChannel, write)
165     {
166       const Java::Env env(jenv);
167 
168       TunnelWriter writer(env, jnative_channel);
169       Java::WritableByteChannel channel(env, writer);
170       return channel.write(jsrc_buffer);
171     }
172   SVN_JAVAHL_JNI_CATCH;
173   return -1;
174 }
175