1 /* ***** BEGIN LICENSE BLOCK *****
2 *   Copyright (C) 2012-2016, Peter Hatina <phatina@gmail.com>
3 *
4 *   This program is free software; you can redistribute it and/or
5 *   modify it under the terms of the GNU General Public License as
6 *   published by the Free Software Foundation; either version 2 of
7 *   the License, or (at your option) any later version.
8 *
9 *   This program is distributed in the hope that it will be useful,
10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 *   GNU General Public License for more details.
13 *
14 *   You should have received a copy of the GNU General Public License
15 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * ***** END LICENSE BLOCK ***** */
17 
18 #include <config.h>
19 #include <cstdio>
20 #include <cstring>
21 #ifdef HAVE_LIBUSB1
22 #  include <iomanip>
23 #  include <sstream>
24 #endif // HAVE_LIBUSB1
25 extern "C" {
26 #  include <dirent.h>
27 #  include <libgen.h>
28 #  include <limits.h>
29 #  include <stdlib.h>
30 #  include <sys/stat.h>
31 #  include <sys/types.h>
32 #  include <unistd.h>
33 }
34 #ifdef HAVE_LIBUSB1
35 #  include <climits>
36 extern "C" {
37 #  include <libmtp.h>
38 #  include <libusb.h>
39 }
40 #endif // HAVE_LIBUSB1
41 #include "simple-mtpfs-log.h"
42 #include "simple-mtpfs-util.h"
43 
44 const std::string devnull = "/dev/null";
45 
46 bool StreamHelper::s_enabled = false;
47 int StreamHelper::s_stdout = -1;
48 int StreamHelper::s_stderr = -1;
49 
on()50 void StreamHelper::on()
51 {
52     if (!s_enabled)
53         return;
54 
55     freopen(devnull.c_str(), "w", stdout);
56     freopen(devnull.c_str(), "w", stderr);
57     dup2(s_stdout, fileno(stdout));
58     dup2(s_stderr, fileno(stderr));
59     close(s_stdout);
60     close(s_stderr);
61     setvbuf(stdout, NULL, _IOLBF, 0);
62     setvbuf(stderr, NULL, _IOLBF, 0);
63 
64     s_enabled = false;
65 }
66 
off()67 void StreamHelper::off()
68 {
69     if (s_enabled)
70         return;
71 
72     fflush(stdout);
73     fflush(stderr);
74     s_stdout = dup(fileno(stdout));
75     s_stderr = dup(fileno(stderr));
76     freopen(devnull.c_str(), "w", stdout);
77     freopen(devnull.c_str(), "w", stderr);
78 
79     s_enabled = true;
80 }
81 
smtpfs_dirname(const std::string & path)82 std::string smtpfs_dirname(const std::string &path)
83 {
84     char *str = strdup(path.c_str());
85     std::string result(dirname(str));
86     free(static_cast<void*>(str));
87     return result;
88 }
89 
smtpfs_basename(const std::string & path)90 std::string smtpfs_basename(const std::string &path)
91 {
92     char *str = strdup(path.c_str());
93     std::string result(basename(str));
94     free(static_cast<void*>(str));
95     return result;
96 }
97 
smtpfs_realpath(const std::string & path)98 std::string smtpfs_realpath(const std::string &path)
99 {
100     char buf[PATH_MAX + 1];
101     char *real_path = realpath(path.c_str(), buf);
102     return std::string(real_path ? buf : "");
103 }
104 
smtpfs_get_tmpdir()105 std::string smtpfs_get_tmpdir()
106 {
107     const char *c_tmp = getenv("TMP");
108     std::string tmp_dir;
109     if (c_tmp) {
110         tmp_dir = smtpfs_realpath(c_tmp);
111     } else {
112         c_tmp = getenv("TMPDIR");
113         if (!c_tmp)
114             c_tmp = TMPDIR;
115         tmp_dir = smtpfs_realpath(c_tmp);
116     }
117 
118     tmp_dir += "/simple-mtpfs-XXXXXX";
119     char *c_tmp_dir = ::mkdtemp(::strdup(tmp_dir.c_str()));
120 
121     tmp_dir.assign(c_tmp_dir);
122     ::free(static_cast<void*>(c_tmp_dir));
123 
124     return tmp_dir;
125 }
126 
smtpfs_create_dir(const std::string & dirname)127 bool smtpfs_create_dir(const std::string &dirname)
128 {
129     return ::mkdir(dirname.c_str(), S_IRWXU) == 0;
130 }
131 
smtpfs_remove_dir(const std::string & dirname)132 bool smtpfs_remove_dir(const std::string &dirname)
133 {
134     DIR *dir;
135     struct dirent *entry;
136     std::string path;
137 
138     dir = ::opendir(dirname.c_str());
139     if (!dir)
140         return false;
141 
142     while ((entry = ::readdir(dir))) {
143         if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
144             path = dirname + "/" + entry->d_name;
145             if (entry->d_type == DT_DIR)
146                 return smtpfs_remove_dir(path);
147             ::unlink(path.c_str());
148         }
149     }
150     ::closedir(dir);
151     ::remove(dirname.c_str());
152     return true;
153 }
154 
smtpfs_usb_devpath(const std::string & path,uint8_t * bnum,uint8_t * dnum)155 bool smtpfs_usb_devpath(const std::string &path, uint8_t *bnum, uint8_t *dnum)
156 {
157     unsigned int bus, dev;
158 #ifdef USB_DEVPATH
159     std::string realpath(smtpfs_realpath(path));
160     if (realpath.empty() ||
161         sscanf(realpath.c_str(), USB_DEVPATH, &bus, &dev) != 2)
162 #endif
163         if (sscanf(path.c_str(), "%u/%u", &bus, &dev) != 2)
164             return false;
165 
166     if (bus > 255 || dev > 255)
167         return false;
168 
169     *bnum = bus;
170     *dnum = dev;
171     return true;
172 }
173 
174 #ifdef HAVE_LIBUSB1
smtpfs_raw_device_new_priv(libusb_device * usb_device)175 LIBMTP_raw_device_t *smtpfs_raw_device_new_priv(libusb_device *usb_device)
176 {
177     if (!usb_device)
178         return nullptr;
179 
180     LIBMTP_raw_device_t *device = static_cast<LIBMTP_raw_device_t*>(
181         malloc(sizeof(LIBMTP_raw_device_t)));
182 
183     if (!device)
184         return nullptr;
185 
186     struct libusb_device_descriptor desc;
187     int err = libusb_get_device_descriptor(usb_device, &desc);
188     if (err != LIBUSB_SUCCESS) {
189         free(static_cast<void*>(device));
190         return nullptr;
191     }
192 
193     device->device_entry.vendor = nullptr;  // TODO: vendor string
194     device->device_entry.vendor_id = desc.idVendor;
195     device->device_entry.product = nullptr; // TODO: product string
196     device->device_entry.product_id = desc.idProduct;
197     device->device_entry.device_flags = 0;
198 
199     device->bus_location = static_cast<uint32_t>(libusb_get_bus_number(usb_device));
200     device->devnum = libusb_get_device_address(usb_device);
201 
202     return device;
203 }
204 
smtpfs_raw_device_new(const std::string & path)205 LIBMTP_raw_device_t *smtpfs_raw_device_new(const std::string &path)
206 {
207     uint8_t bnum, dnum;
208     if (!smtpfs_usb_devpath(path, &bnum, &dnum))
209         return nullptr;
210 
211     if (libusb_init(NULL) != 0)
212         return nullptr;
213 
214     libusb_device **dev_list;
215     ssize_t num_devs = libusb_get_device_list(NULL, &dev_list);
216     if (!num_devs) {
217         libusb_exit(NULL);
218         return nullptr;
219     }
220 
221     libusb_device *dev = nullptr;
222     for (auto i = 0; i < num_devs; ++i) {
223         dev = dev_list[i];
224         if (bnum == libusb_get_bus_number(dev_list[i]) &&
225             dnum == libusb_get_device_address(dev_list[i]))
226             break;
227         dev = nullptr;
228     }
229 
230     LIBMTP_raw_device_t *raw_device = smtpfs_raw_device_new_priv(dev);
231 
232     libusb_free_device_list(dev_list, 0);
233     libusb_exit(NULL);
234 
235     return raw_device;
236 }
237 
smtpfs_reset_device(LIBMTP_raw_device_t * device)238 bool smtpfs_reset_device(LIBMTP_raw_device_t *device)
239 {
240     if (libusb_init(NULL) != 0)
241         return false;
242 
243     libusb_device **dev_list;
244     ssize_t num_devs = libusb_get_device_list(NULL, &dev_list);
245     if (!num_devs) {
246         libusb_exit(NULL);
247         return false;
248     }
249 
250     libusb_device_handle *dev_handle = nullptr;
251     for (auto i = 0; i < num_devs; ++i) {
252         uint8_t bnum = libusb_get_bus_number(dev_list[i]);
253         uint8_t dnum = libusb_get_device_address(dev_list[i]);
254 
255         if (static_cast<uint32_t>(bnum) == device->bus_location &&
256             dnum == device->devnum)
257         {
258             libusb_open(dev_list[i], &dev_handle);
259             libusb_reset_device(dev_handle);
260             libusb_close(dev_handle);
261             break;
262         }
263     }
264 
265     libusb_free_device_list(dev_list, 0);
266     libusb_exit(NULL);
267 
268     return true;
269 }
270 
smtpfs_raw_device_free(LIBMTP_raw_device_t * device)271 void smtpfs_raw_device_free(LIBMTP_raw_device_t *device)
272 {
273     if (!device)
274         return;
275 
276     free(static_cast<void*>(device->device_entry.vendor));
277     free(static_cast<void*>(device->device_entry.product));
278     free(static_cast<void*>(device));
279 }
280 #endif // HAVE_LIBUSB1
281 
smtpfs_check_dir(const std::string & path)282 bool smtpfs_check_dir(const std::string &path)
283 {
284     struct stat buf;
285     if (::stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode)
286         && ::access(path.c_str(), R_OK | W_OK | X_OK) == 0)
287     {
288         return true;
289     }
290 
291     return false;
292 }
293