1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4 
5 #include <OpenImageIO/imageio.h>
6 
7 #include "socket_pvt.h"
8 
9 
10 OIIO_PLUGIN_NAMESPACE_BEGIN
11 
12 // Export version number and create function symbols
13 OIIO_PLUGIN_EXPORTS_BEGIN
14 
15 OIIO_EXPORT int socket_imageio_version = OIIO_PLUGIN_VERSION;
16 
17 OIIO_EXPORT const char*
socket_imageio_library_version()18 socket_imageio_library_version()
19 {
20     return nullptr;
21 }
22 
23 OIIO_EXPORT ImageInput*
socket_input_imageio_create()24 socket_input_imageio_create()
25 {
26     return new SocketInput;
27 }
28 
29 OIIO_EXPORT const char* socket_input_extensions[] = { "socket", nullptr };
30 
31 OIIO_PLUGIN_EXPORTS_END
32 
33 
34 
SocketInput()35 SocketInput::SocketInput()
36     : socket(io)
37 {
38 }
39 
40 
41 
42 bool
valid_file(const std::string & filename) const43 SocketInput::valid_file(const std::string& filename) const
44 {
45     // Pass it a configuration request that includes a "nowait" option
46     // so that it returns immediately rather than waiting for a socket
47     // connection that doesn't yet exist.
48     ImageSpec config;
49     config.attribute("nowait", (int)1);
50 
51     ImageSpec tmpspec;
52     bool ok = const_cast<SocketInput*>(this)->open(filename, tmpspec, config);
53     if (ok)
54         const_cast<SocketInput*>(this)->close();
55     return ok;
56 }
57 
58 
59 
60 bool
open(const std::string & name,ImageSpec & newspec)61 SocketInput::open(const std::string& name, ImageSpec& newspec)
62 {
63     return open(name, newspec, ImageSpec());
64 }
65 
66 
67 
68 bool
open(const std::string & name,ImageSpec & newspec,const ImageSpec & config)69 SocketInput::open(const std::string& name, ImageSpec& newspec,
70                   const ImageSpec& config)
71 {
72     // If there is a nonzero "nowait" request in the configuration, just
73     // return immediately.
74     if (config.get_int_attribute("nowait", 0)) {
75         return false;
76     }
77 
78     if (!(accept_connection(name) && get_spec_from_client(newspec))) {
79         return false;
80     }
81     // Also send information about endianess etc.
82 
83     m_spec = newspec;
84 
85     return true;
86 }
87 
88 
89 
90 bool
read_native_scanline(int subimage,int miplevel,int,int,void * data)91 SocketInput::read_native_scanline(int subimage, int miplevel, int /*y*/,
92                                   int /*z*/, void* data)
93 {
94     lock_guard lock(m_mutex);
95     if (!seek_subimage(subimage, miplevel))
96         return false;
97     try {
98         boost::asio::read(socket, buffer(reinterpret_cast<char*>(data),
99                                          m_spec.scanline_bytes()));
100     } catch (boost::system::system_error& err) {
101         errorf("Error while reading: %s", err.what());
102         return false;
103     } catch (...) {
104         errorf("Error while reading: unknown exception");
105         return false;
106     }
107 
108     return true;
109 }
110 
111 
112 
113 bool
read_native_tile(int subimage,int miplevel,int,int,int,void * data)114 SocketInput::read_native_tile(int subimage, int miplevel, int /*x*/, int /*y*/,
115                               int /*z*/, void* data)
116 {
117     lock_guard lock(m_mutex);
118     if (!seek_subimage(subimage, miplevel))
119         return false;
120     try {
121         boost::asio::read(socket, buffer(reinterpret_cast<char*>(data),
122                                          m_spec.tile_bytes()));
123     } catch (boost::system::system_error& err) {
124         errorf("Error while reading: %s", err.what());
125         return false;
126     } catch (...) {
127         errorf("Error while reading: unknown exception");
128         return false;
129     }
130 
131     return true;
132 }
133 
134 
135 
136 bool
close()137 SocketInput::close()
138 {
139     socket.close();
140     return true;
141 }
142 
143 
144 
145 bool
accept_connection(const std::string & name)146 SocketInput::accept_connection(const std::string& name)
147 {
148     std::map<std::string, std::string> rest_args;
149     std::string baseurl;
150     rest_args["port"] = socket_pvt::default_port;
151     rest_args["host"] = socket_pvt::default_host;
152 
153     if (!Strutil::get_rest_arguments(name, baseurl, rest_args)) {
154         errorf("Invalid 'open ()' argument: %s", name);
155         return false;
156     }
157 
158     int port = Strutil::stoi(rest_args["port"]);
159 
160     try {
161         acceptor = std::shared_ptr<ip::tcp::acceptor>(
162             new ip::tcp::acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), port)));
163         acceptor->accept(socket);
164     } catch (boost::system::system_error& err) {
165         errorf("Error while accepting: %s", err.what());
166         return false;
167     } catch (...) {
168         errorf("Error while accepting: unknown exception");
169         return false;
170     }
171 
172     return true;
173 }
174 
175 
176 
177 bool
get_spec_from_client(ImageSpec & spec)178 SocketInput::get_spec_from_client(ImageSpec& spec)
179 {
180     try {
181         int spec_length;
182 
183         boost::asio::read(socket, buffer(reinterpret_cast<char*>(&spec_length),
184                                          sizeof(boost::uint32_t)));
185 
186         char* spec_xml = new char[spec_length + 1];
187         boost::asio::read(socket, buffer(spec_xml, spec_length));
188 
189         spec.from_xml(spec_xml);
190         delete[] spec_xml;
191     } catch (boost::system::system_error& err) {
192         errorf("Error while get_spec_from_client: %s", err.what());
193         return false;
194     } catch (...) {
195         errorf("Error while get_spec_from_client: unknown exception");
196         return false;
197     }
198 
199     return true;
200 }
201 
202 OIIO_PLUGIN_NAMESPACE_END
203