TDE graphics utilities
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1103 lines
31KB

  1. /*
  2. Copyright (C) 2001 The Kompany
  3. 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il>
  4. 2001-2007 Marcus Meissner <marcus@jet.franken.de>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. */
  17. #include <stdlib.h>
  18. #include <unistd.h>
  19. #include <stdio.h>
  20. #include <fcntl.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <signal.h>
  24. #include <errno.h>
  25. #include <tqfile.h>
  26. #include <tqtextstream.h>
  27. #include <kdebug.h>
  28. #include <kinstance.h>
  29. #include <kstandarddirs.h>
  30. #include <tdeconfig.h>
  31. #include <ksimpleconfig.h>
  32. #include <klocale.h>
  33. #include <kprotocolinfo.h>
  34. #include <tdeio/slaveconfig.h>
  35. #include <config.h>
  36. #include "kamera.h"
  37. #define MAXIDLETIME 30 /* seconds */
  38. #define tocstr(x) ((x).local8Bit())
  39. using namespace TDEIO;
  40. extern "C"
  41. {
  42. KDE_EXPORT int kdemain(int argc, char **argv);
  43. #ifdef HAVE_GPHOTO2_5
  44. static void frontendCameraStatus(GPContext *context, const char *status, void *data);
  45. static unsigned int frontendProgressStart(
  46. GPContext *context, float totalsize, const char *status,
  47. void *data
  48. );
  49. #else
  50. static void frontendCameraStatus(GPContext *context, const char *format, va_list args, void *data);
  51. static unsigned int frontendProgressStart(
  52. GPContext *context, float totalsize, const char *format,
  53. va_list args, void *data
  54. );
  55. #endif
  56. static void frontendProgressUpdate(
  57. GPContext *context, unsigned int id, float current, void *data
  58. );
  59. }
  60. int kdemain(int argc, char **argv)
  61. {
  62. TDEInstance instance("tdeio_kamera");
  63. if(argc != 4) {
  64. kdDebug(7123) << "Usage: tdeio_kamera protocol "
  65. "domain-socket1 domain-socket2" << endl;
  66. exit(-1);
  67. }
  68. KameraProtocol slave(argv[2], argv[3]);
  69. slave.dispatchLoop();
  70. return 0;
  71. }
  72. KameraProtocol::KameraProtocol(const TQCString &pool, const TQCString &app)
  73. : SlaveBase("camera", pool, app),
  74. m_camera(NULL)
  75. {
  76. // attempt to initialize libgphoto2 and chosen camera (requires locking)
  77. // (will init m_camera, since the m_camera's configuration is empty)
  78. m_camera = 0;
  79. m_file = NULL;
  80. m_config = new KSimpleConfig(KProtocolInfo::config("camera"));
  81. m_context = gp_context_new();
  82. actiondone = true;
  83. cameraopen = false;
  84. m_modelSpecified = true;
  85. m_lockfile = locateLocal("tmp", "kamera");
  86. idletime = 0;
  87. }
  88. // This handler is getting called every second. We use it to do the
  89. // delayed close of the camera.
  90. // Logic is:
  91. // - No more requests in the queue (signaled by actiondone) AND
  92. // - We are MAXIDLETIME seconds idle OR
  93. // - Another slave wants to have access to the camera.
  94. //
  95. // The existance of a lockfile is used to signify "please give up camera".
  96. //
  97. void KameraProtocol::special(const TQByteArray&) {
  98. kdDebug(7123) << "KameraProtocol::special() at " << getpid() << endl;
  99. if (!actiondone && cameraopen) {
  100. struct stat stbuf;
  101. if ((-1!=::stat(m_lockfile.utf8(),&stbuf)) || (idletime++ >= MAXIDLETIME)) {
  102. kdDebug(7123) << "KameraProtocol::special() closing camera." << endl;
  103. closeCamera();
  104. setTimeoutSpecialCommand(-1);
  105. } else {
  106. // continue to wait
  107. setTimeoutSpecialCommand(1);
  108. }
  109. } else {
  110. // We let it run until the slave gets no actions anymore.
  111. setTimeoutSpecialCommand(1);
  112. }
  113. actiondone = false;
  114. }
  115. KameraProtocol::~KameraProtocol()
  116. {
  117. kdDebug(7123) << "KameraProtocol::~KameraProtocol()" << endl;
  118. delete m_config;
  119. if(m_camera) {
  120. closeCamera();
  121. gp_camera_free(m_camera);
  122. m_camera = NULL;
  123. }
  124. }
  125. // initializes the camera for usage - should be done before operations over the wire
  126. bool KameraProtocol::openCamera(TQString &str) {
  127. idletime = 0;
  128. actiondone = true;
  129. if (!m_camera) {
  130. reparseConfiguration();
  131. } else {
  132. if (!cameraopen) {
  133. int ret, tries = 15;
  134. kdDebug(7123) << "KameraProtocol::openCamera at " << getpid() << endl;
  135. while (tries--) {
  136. ret = gp_camera_init(m_camera, m_context);
  137. if ( (ret == GP_ERROR_IO_USB_CLAIM) ||
  138. (ret == GP_ERROR_IO_LOCK)) {
  139. // just create / touch if not there
  140. int fd = ::open(m_lockfile.utf8(),O_CREAT|O_WRONLY,0600);
  141. if (fd != -1) ::close(fd);
  142. ::sleep(1);
  143. kdDebug(7123) << "openCamera at " << getpid() << "- busy, ret " << ret << ", trying again." << endl;
  144. continue;
  145. }
  146. if (ret == GP_OK) break;
  147. str = gp_result_as_string(ret);
  148. return false;
  149. }
  150. ::unlink(m_lockfile.utf8());
  151. setTimeoutSpecialCommand(1);
  152. kdDebug(7123) << "openCamera succeeded at " << getpid() << endl;
  153. if (!m_modelSpecified) {
  154. gp_camera_get_abilities(m_camera, &m_abilities);
  155. m_modelSpecified = true;
  156. }
  157. cameraopen = true;
  158. }
  159. }
  160. return true;
  161. }
  162. // should be done after operations over the wire
  163. void KameraProtocol::closeCamera(void)
  164. {
  165. int gpr;
  166. if (!m_camera)
  167. return;
  168. kdDebug(7123) << "KameraProtocol::closeCamera at " << getpid() << endl;
  169. if ((gpr=gp_camera_exit(m_camera,m_context))!=GP_OK) {
  170. kdDebug(7123) << "closeCamera failed with " << gp_result_as_string(gpr) << endl;
  171. }
  172. // HACK: gp_camera_exit() in gp 2.0 does not close the port if there
  173. // is no camera_exit function.
  174. gp_port_close(m_camera->port);
  175. cameraopen = false;
  176. return;
  177. }
  178. static TQString fix_foldername(TQString ofolder) {
  179. TQString folder = ofolder;
  180. if (folder.length() > 1) {
  181. while ((folder.length()>1) && (folder.right(1) == "/"))
  182. folder = folder.left(folder.length()-1);
  183. }
  184. if (folder.length() == 0)
  185. folder = "/";
  186. return folder;
  187. }
  188. // The KIO slave "get" function (starts a download from the camera)
  189. // The actual returning of the data is done in the frontend callback functions.
  190. void KameraProtocol::get(const KURL &url)
  191. {
  192. kdDebug(7123) << "KameraProtocol::get(" << url.path() << ")" << endl;
  193. CameraFileType fileType;
  194. int gpr;
  195. if (url.host().isEmpty()) {
  196. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  197. return;
  198. }
  199. if(!openCamera()) {
  200. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  201. return;
  202. }
  203. // fprintf(stderr,"get(%s)\n",url.path().latin1());
  204. #define GPHOTO_TEXT_FILE(xx) \
  205. if (!url.path().compare("/" #xx ".txt")) { \
  206. CameraText xx; \
  207. gpr = gp_camera_get_##xx(m_camera, &xx, m_context); \
  208. if (gpr != GP_OK) { \
  209. error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); \
  210. return; \
  211. } \
  212. TQByteArray chunkDataBuffer; \
  213. chunkDataBuffer.setRawData(xx.text, strlen(xx.text)); \
  214. data(chunkDataBuffer); \
  215. processedSize(strlen(xx.text)); \
  216. chunkDataBuffer.resetRawData(xx.text, strlen(xx.text)); \
  217. finished(); \
  218. return; \
  219. }
  220. GPHOTO_TEXT_FILE(about);
  221. GPHOTO_TEXT_FILE(manual);
  222. GPHOTO_TEXT_FILE(summary);
  223. #undef GPHOTO_TEXT_FILE
  224. // emit info message
  225. infoMessage( i18n("Retrieving data from camera <b>%1</b>").arg(url.user()) );
  226. // Note: There's no need to re-read directory for each get() anymore
  227. gp_file_new(&m_file);
  228. // emit the total size (we must do it before sending data to allow preview)
  229. CameraFileInfo info;
  230. gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context);
  231. if (gpr != GP_OK) {
  232. // fprintf(stderr,"Folder %s / File %s not found, gpr is %d\n",folder.latin1(), url.fileName().latin1(), gpr);
  233. gp_file_unref(m_file);
  234. if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
  235. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  236. else
  237. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  238. return;
  239. }
  240. // at last, a proper API to determine whether a thumbnail was requested.
  241. if(cameraSupportsPreview() && metaData("thumbnail") == "1") {
  242. kdDebug(7123) << "get() retrieving the thumbnail" << endl;
  243. fileType = GP_FILE_TYPE_PREVIEW;
  244. if (info.preview.fields & GP_FILE_INFO_SIZE)
  245. totalSize(info.preview.size);
  246. if (info.preview.fields & GP_FILE_INFO_TYPE)
  247. mimeType(info.preview.type);
  248. } else {
  249. kdDebug(7123) << "get() retrieving the full-scale photo" << endl;
  250. fileType = GP_FILE_TYPE_NORMAL;
  251. if (info.file.fields & GP_FILE_INFO_SIZE)
  252. totalSize(info.file.size);
  253. if (info.preview.fields & GP_FILE_INFO_TYPE)
  254. mimeType(info.file.type);
  255. }
  256. // fetch the data
  257. m_fileSize = 0;
  258. gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context);
  259. if ( (gpr == GP_ERROR_NOT_SUPPORTED) &&
  260. (fileType == GP_FILE_TYPE_PREVIEW)
  261. ) {
  262. // If we get here, the file info command information
  263. // will either not be used, or still valid.
  264. fileType = GP_FILE_TYPE_NORMAL;
  265. gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context);
  266. }
  267. switch(gpr) {
  268. case GP_OK:
  269. break;
  270. case GP_ERROR_FILE_NOT_FOUND:
  271. case GP_ERROR_DIRECTORY_NOT_FOUND:
  272. gp_file_unref(m_file);
  273. m_file = NULL;
  274. error(TDEIO::ERR_DOES_NOT_EXIST, url.fileName());
  275. return ;
  276. default:
  277. gp_file_unref(m_file);
  278. m_file = NULL;
  279. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  280. return;
  281. }
  282. // emit the mimetype
  283. // NOTE: we must first get the file, so that CameraFile->name would be set
  284. const char *fileMimeType;
  285. gp_file_get_mime_type(m_file, &fileMimeType);
  286. mimeType(fileMimeType);
  287. // We need to pass left over data here. Some camera drivers do not
  288. // implement progress callbacks!
  289. const char *fileData;
  290. long unsigned int fileSize;
  291. // This merely returns us a pointer to gphoto's internal data
  292. // buffer -- there's no expensive memcpy
  293. gpr = gp_file_get_data_and_size(m_file, &fileData, &fileSize);
  294. if (gpr != GP_OK) {
  295. kdDebug(7123) << "get():: get_data_and_size failed." << endl;
  296. gp_file_free(m_file);
  297. m_file = NULL;
  298. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  299. return;
  300. }
  301. // make sure we're not sending zero-sized chunks (=EOF)
  302. // also make sure we send only if the progress did not send the data
  303. // already.
  304. if ((fileSize > 0) && (fileSize - m_fileSize)>0) {
  305. unsigned long written = 0;
  306. TQByteArray chunkDataBuffer;
  307. // We need to split it up here. Someone considered it funny
  308. // to discard any data() larger than 16MB.
  309. //
  310. // So nearly any Movie will just fail....
  311. while (written < fileSize-m_fileSize) {
  312. unsigned long towrite = 1024*1024; // 1MB
  313. if (towrite > fileSize-m_fileSize-written)
  314. towrite = fileSize-m_fileSize-written;
  315. chunkDataBuffer.setRawData(fileData + m_fileSize + written, towrite);
  316. processedSize(m_fileSize + written + towrite);
  317. data(chunkDataBuffer);
  318. chunkDataBuffer.resetRawData(fileData + m_fileSize + written, towrite);
  319. written += towrite;
  320. }
  321. m_fileSize = fileSize;
  322. setFileSize(fileSize);
  323. }
  324. finished();
  325. gp_file_unref(m_file); /* just unref, might be stored in fs */
  326. m_file = NULL;
  327. }
  328. // The KIO slave "stat" function.
  329. void KameraProtocol::stat(const KURL &url)
  330. {
  331. kdDebug(7123) << "stat(\"" << url.path() << "\")" << endl;
  332. if (url.path() == "") {
  333. KURL rooturl(url);
  334. kdDebug(7123) << "redirecting to /" << endl;
  335. rooturl.setPath("/");
  336. rooturl.setHost(url.host());
  337. rooturl.setUser(url.user());
  338. redirection(rooturl);
  339. finished();
  340. return;
  341. }
  342. if(url.path() == "/")
  343. statRoot();
  344. else
  345. statRegular(url);
  346. }
  347. // Implements stat("/") -- which always returns the same value.
  348. void KameraProtocol::statRoot(void)
  349. {
  350. UDSEntry entry;
  351. UDSAtom atom;
  352. atom.m_uds = UDS_NAME;
  353. atom.m_str = "/";
  354. entry.append(atom);
  355. atom.m_uds = UDS_FILE_TYPE;
  356. atom.m_long = S_IFDIR;
  357. entry.append(atom);
  358. atom.m_uds = UDS_ACCESS;
  359. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH |
  360. S_IWUSR | S_IWGRP | S_IWOTH;
  361. entry.append(atom);
  362. statEntry(entry);
  363. finished();
  364. // This call happens on autodetect by kdemm. So close the camera, but
  365. // only if no more requests are pending.
  366. idletime = MAXIDLETIME;
  367. }
  368. // Implements a regular stat() of a file / directory, returning all we know about it
  369. void KameraProtocol::statRegular(const KURL &url)
  370. {
  371. UDSEntry entry;
  372. int gpr;
  373. kdDebug(7123) << "statRegular(\"" << url.path() << "\")" << endl;
  374. if (openCamera() == false) {
  375. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  376. return;
  377. }
  378. // fprintf(stderr,"statRegular(%s)\n",url.path().latin1());
  379. // Is "url" a directory?
  380. CameraList *dirList;
  381. gp_list_new(&dirList);
  382. kdDebug(7123) << "statRegular() Requesting directories list for " << url.directory() << endl;
  383. gpr = gp_camera_folder_list_folders(m_camera, tocstr(fix_foldername(url.directory(false))), dirList, m_context);
  384. if (gpr != GP_OK) {
  385. if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
  386. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  387. else
  388. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  389. gp_list_free(dirList);
  390. return;
  391. }
  392. #define GPHOTO_TEXT_FILE(xx) \
  393. if (!url.path().compare("/"#xx".txt")) { \
  394. CameraText xx; \
  395. gpr = gp_camera_get_about(m_camera, &xx, m_context); \
  396. if (gpr != GP_OK) { \
  397. error(TDEIO::ERR_DOES_NOT_EXIST, url.fileName()); \
  398. return; \
  399. } \
  400. translateTextToUDS(entry,#xx".txt",xx.text); \
  401. statEntry(entry); \
  402. finished(); \
  403. return; \
  404. }
  405. GPHOTO_TEXT_FILE(about);
  406. GPHOTO_TEXT_FILE(manual);
  407. GPHOTO_TEXT_FILE(summary);
  408. #undef GPHOTO_TEXT_FILE
  409. const char *name;
  410. for(int i = 0; i < gp_list_count(dirList); i++) {
  411. gp_list_get_name(dirList, i, &name);
  412. if (url.fileName().compare(name) == 0) {
  413. gp_list_free(dirList);
  414. UDSEntry entry;
  415. translateDirectoryToUDS(entry, url.fileName());
  416. statEntry(entry);
  417. finished();
  418. return;
  419. }
  420. }
  421. gp_list_free(dirList);
  422. // Is "url" a file?
  423. CameraFileInfo info;
  424. gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context);
  425. if (gpr != GP_OK) {
  426. if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
  427. error(TDEIO::ERR_DOES_NOT_EXIST, url.path());
  428. else
  429. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  430. return;
  431. }
  432. translateFileToUDS(entry, info, url.fileName());
  433. statEntry(entry);
  434. finished();
  435. }
  436. // The KIO slave "del" function.
  437. void KameraProtocol::del(const KURL &url, bool isFile)
  438. {
  439. kdDebug(7123) << "KameraProtocol::del(" << url.path() << ")" << endl;
  440. if(!openCamera()) {
  441. error(TDEIO::ERR_CANNOT_DELETE, url.fileName());
  442. return;
  443. }
  444. if (!cameraSupportsDel()) {
  445. error(TDEIO::ERR_CANNOT_DELETE, url.fileName());
  446. return;
  447. }
  448. if(isFile){
  449. CameraList *list;
  450. gp_list_new(&list);
  451. int ret;
  452. ret = gp_camera_file_delete(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), m_context);
  453. if(ret != GP_OK) {
  454. error(TDEIO::ERR_CANNOT_DELETE, url.fileName());
  455. } else {
  456. finished();
  457. }
  458. }
  459. }
  460. // The KIO slave "listDir" function.
  461. void KameraProtocol::listDir(const KURL &url)
  462. {
  463. kdDebug(7123) << "KameraProtocol::listDir(" << url.path() << ")" << endl;
  464. if (url.host().isEmpty()) {
  465. KURL xurl;
  466. // List the available cameras
  467. TQStringList groupList = m_config->groupList();
  468. kdDebug(7123) << "Found cameras: " << groupList.join(", ") << endl;
  469. TQStringList::Iterator it;
  470. UDSEntry entry;
  471. UDSAtom atom;
  472. /*
  473. * What we do:
  474. * - Autodetect cameras and remember them with their ports.
  475. * - List all saved and possible offline cameras.
  476. * - List all autodetected and not yet printed cameras.
  477. */
  478. TQMap<TQString,TQString> ports, names;
  479. TQMap<TQString,int> modelcnt;
  480. /* Autodetect USB cameras ... */
  481. GPContext *glob_context = NULL;
  482. int i, count;
  483. CameraList *list;
  484. CameraAbilitiesList *al;
  485. GPPortInfoList *il;
  486. gp_list_new (&list);
  487. gp_abilities_list_new (&al);
  488. gp_abilities_list_load (al, glob_context);
  489. gp_port_info_list_new (&il);
  490. gp_port_info_list_load (il);
  491. gp_abilities_list_detect (al, il, list, glob_context);
  492. gp_abilities_list_free (al);
  493. gp_port_info_list_free (il);
  494. count = gp_list_count (list);
  495. for (i = 0 ; i<count ; i++) {
  496. const char *model, *value;
  497. gp_list_get_name (list, i, &model);
  498. gp_list_get_value (list, i, &value);
  499. ports[value] = model;
  500. // NOTE: We might get different ports than usb: later!
  501. if (strcmp(value, "usb:"))
  502. names[model] = value;
  503. /* Save them, even though we can autodetect them for
  504. * offline listing.
  505. */
  506. m_config->setGroup(model);
  507. m_config->writeEntry("Model",model);
  508. m_config->writeEntry("Path",value);
  509. modelcnt[model]++;
  510. }
  511. gp_list_free (list);
  512. /* Avoid duplicated entry for usb: and usb:001,042 entries. */
  513. if (ports.contains("usb:") && names[ports["usb:"]]!="usb:")
  514. ports.remove("usb:");
  515. for (it = groupList.begin(); it != groupList.end(); it++) {
  516. TQString m_cfgPath;
  517. if (*it == "<default>")
  518. continue;
  519. m_config->setGroup(*it);
  520. m_cfgPath = m_config->readEntry("Path");
  521. /* If autodetect by USB autodetect ... skip it here.
  522. * We leave unattached USB cameras in here, because the user
  523. * might plug them in later and does not want to press reload.
  524. * We add them with port "usb:".
  525. */
  526. if (modelcnt[*it] > 0)
  527. continue;
  528. entry.clear();
  529. atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR;entry.append(atom);
  530. atom.m_uds = UDS_NAME;atom.m_str = *it;entry.append(atom);
  531. atom.m_uds = UDS_ACCESS;
  532. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH |
  533. S_IWUSR | S_IWGRP | S_IWOTH;
  534. entry.append(atom);
  535. atom.m_uds = UDS_URL;
  536. xurl.setProtocol("camera");
  537. xurl.setUser(*it);
  538. /* Avoid setting usb:xxx,yyy. */
  539. if (m_cfgPath.contains("usb:")>0) {
  540. names[*it] = "usb:";
  541. xurl.setHost("usb:");
  542. } else {
  543. xurl.setHost(m_cfgPath);
  544. }
  545. xurl.setPath("/");
  546. atom.m_str = xurl.url();
  547. entry.append(atom);
  548. listEntry(entry, false);
  549. }
  550. TQMap<TQString,TQString>::iterator portsit;
  551. for (portsit = ports.begin(); portsit != ports.end(); portsit++) {
  552. entry.clear();
  553. atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR; entry.append(atom);
  554. atom.m_uds = UDS_NAME;atom.m_str = portsit.data();entry.append(atom);
  555. atom.m_uds = UDS_ACCESS;
  556. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH |
  557. S_IWUSR | S_IWGRP | S_IWOTH;
  558. entry.append(atom);
  559. atom.m_uds = UDS_URL;
  560. xurl.setProtocol("camera");
  561. xurl.setHost(portsit.key());
  562. xurl.setUser(portsit.data());
  563. xurl.setPath("/");
  564. atom.m_str = xurl.url();
  565. entry.append(atom);
  566. listEntry(entry, false);
  567. }
  568. listEntry(entry, true);
  569. finished();
  570. return;
  571. }
  572. if (url.path() == "") {
  573. KURL rooturl(url);
  574. kdDebug(7123) << "redirecting to /" << endl;
  575. rooturl.setPath("/");
  576. rooturl.setHost(url.host());
  577. rooturl.setUser(url.user());
  578. redirection(rooturl);
  579. finished();
  580. return;
  581. }
  582. if (!openCamera()) {
  583. error(TDEIO::ERR_COULD_NOT_READ,url.path());
  584. return;
  585. }
  586. CameraList *dirList;
  587. CameraList *fileList;
  588. CameraList *specialList;
  589. gp_list_new(&dirList);
  590. gp_list_new(&fileList);
  591. gp_list_new(&specialList);
  592. int gpr;
  593. if (!url.path().compare("/")) {
  594. CameraText text;
  595. if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context))
  596. gp_list_append(specialList,"manual.txt",NULL);
  597. if (GP_OK == gp_camera_get_about(m_camera, &text, m_context))
  598. gp_list_append(specialList,"about.txt",NULL);
  599. if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context))
  600. gp_list_append(specialList,"summary.txt",NULL);
  601. }
  602. gpr = readCameraFolder(url.path(), dirList, fileList);
  603. if(gpr != GP_OK) {
  604. kdDebug(7123) << "read Camera Folder failed:" << gp_result_as_string(gpr) <<endl;
  605. gp_list_free(dirList);
  606. gp_list_free(fileList);
  607. gp_list_free(specialList);
  608. error(TDEIO::ERR_COULD_NOT_READ, gp_result_as_string(gpr));
  609. return;
  610. }
  611. totalSize(gp_list_count(specialList) + gp_list_count(dirList) + gp_list_count(fileList));
  612. UDSEntry entry;
  613. const char *name;
  614. for(int i = 0; i < gp_list_count(dirList); ++i) {
  615. gp_list_get_name(dirList, i, &name);
  616. translateDirectoryToUDS(entry, TQString::fromLocal8Bit(name));
  617. listEntry(entry, false);
  618. }
  619. CameraFileInfo info;
  620. for(int i = 0; i < gp_list_count(fileList); ++i) {
  621. gp_list_get_name(fileList, i, &name);
  622. // we want to know more info about files (size, type...)
  623. gp_camera_file_get_info(m_camera, tocstr(url.path()), name, &info, m_context);
  624. translateFileToUDS(entry, info, TQString::fromLocal8Bit(name));
  625. listEntry(entry, false);
  626. }
  627. if (!url.path().compare("/")) {
  628. CameraText text;
  629. if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) {
  630. translateTextToUDS(entry, "manual.txt", text.text);
  631. listEntry(entry, false);
  632. }
  633. if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) {
  634. translateTextToUDS(entry, "about.txt", text.text);
  635. listEntry(entry, false);
  636. }
  637. if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) {
  638. translateTextToUDS(entry, "summary.txt", text.text);
  639. listEntry(entry, false);
  640. }
  641. }
  642. gp_list_free(fileList);
  643. gp_list_free(dirList);
  644. gp_list_free(specialList);
  645. listEntry(entry, true); // 'entry' is not used in this case - we only signal list completion
  646. finished();
  647. }
  648. void KameraProtocol::setHost(const TQString& host, int port, const TQString& user, const TQString& pass )
  649. {
  650. kdDebug(7123) << "KameraProtocol::setHost(" << host << ", " << port << ", " << user << ", " << pass << ")" << endl;
  651. int gpr, idx;
  652. if (!host.isEmpty()) {
  653. kdDebug(7123) << "model is " << user << ", port is " << host << endl;
  654. if (m_camera) {
  655. kdDebug(7123) << "Configuration change detected" << endl;
  656. closeCamera();
  657. gp_camera_unref(m_camera);
  658. m_camera = NULL;
  659. infoMessage( i18n("Reinitializing camera") );
  660. } else {
  661. kdDebug(7123) << "Initializing camera" << endl;
  662. infoMessage( i18n("Initializing camera") );
  663. }
  664. // fetch abilities
  665. CameraAbilitiesList *abilities_list;
  666. gp_abilities_list_new(&abilities_list);
  667. gp_abilities_list_load(abilities_list, m_context);
  668. idx = gp_abilities_list_lookup_model(abilities_list, tocstr(user));
  669. if (idx < 0) {
  670. gp_abilities_list_free(abilities_list);
  671. kdDebug(7123) << "Unable to get abilities for model: " << user << ", falling back to automatic model detection" << endl;
  672. m_modelSpecified = false;
  673. }
  674. if (m_modelSpecified) {
  675. gp_abilities_list_get_abilities(abilities_list, idx, &m_abilities);
  676. gp_abilities_list_free(abilities_list);
  677. }
  678. // fetch port
  679. GPPortInfoList *port_info_list;
  680. GPPortInfo port_info;
  681. gp_port_info_list_new(&port_info_list);
  682. gp_port_info_list_load(port_info_list);
  683. idx = gp_port_info_list_lookup_path(port_info_list, tocstr(host));
  684. /* Handle erronously passed usb:XXX,YYY */
  685. if ((idx < 0) && host.startsWith("usb:"))
  686. idx = gp_port_info_list_lookup_path(port_info_list, "usb:");
  687. if (idx < 0) {
  688. gp_port_info_list_free(port_info_list);
  689. kdDebug(7123) << "Unable to get port info for path: " << host << endl;
  690. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(idx));
  691. return;
  692. }
  693. gp_port_info_list_get_info(port_info_list, idx, &port_info);
  694. // create a new camera object
  695. gpr = gp_camera_new(&m_camera);
  696. if(gpr != GP_OK) {
  697. gp_port_info_list_free(port_info_list);
  698. error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr));
  699. return;
  700. }
  701. // register gphoto2 callback functions
  702. gp_context_set_status_func(m_context, frontendCameraStatus, this);
  703. gp_context_set_progress_funcs(m_context, frontendProgressStart, frontendProgressUpdate, NULL, this);
  704. // gp_camera_set_message_func(m_camera, ..., this)
  705. // set model and port
  706. if (m_modelSpecified) {
  707. gp_camera_set_abilities(m_camera, m_abilities);
  708. }
  709. gp_camera_set_port_info(m_camera, port_info);
  710. gp_camera_set_port_speed(m_camera, 0); // TODO: the value needs to be configurable
  711. kdDebug(7123) << "Opening camera model " << user << " at " << host << endl;
  712. gp_port_info_list_free(port_info_list);
  713. TQString errstr;
  714. if (!openCamera(errstr)) {
  715. kdDebug(7123) << "Unable to init camera: " << gp_result_as_string(gpr) << endl;
  716. error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, errstr);
  717. gp_camera_exit(m_camera, m_context);
  718. return;
  719. }
  720. }
  721. }
  722. void KameraProtocol::reparseConfiguration(void)
  723. {
  724. // we have no global config, do we?
  725. }
  726. // translate a simple text to a UDS entry
  727. void KameraProtocol::translateTextToUDS(UDSEntry &udsEntry, const TQString &fn,
  728. const char *text
  729. ) {
  730. UDSAtom atom;
  731. udsEntry.clear();
  732. atom.m_uds = UDS_FILE_TYPE; // UDS type
  733. atom.m_long = S_IFREG; // file
  734. udsEntry.append(atom);
  735. atom.m_uds = UDS_NAME;
  736. atom.m_str = fn;
  737. udsEntry.append(atom);
  738. atom.m_uds = UDS_SIZE;
  739. atom.m_long = strlen(text);
  740. udsEntry.append(atom);
  741. atom.m_uds = UDS_ACCESS;
  742. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH;
  743. udsEntry.append(atom);
  744. }
  745. // translate a CameraFileInfo to a UDSEntry which we can return as a directory listing entry
  746. void KameraProtocol::translateFileToUDS(UDSEntry &udsEntry, const CameraFileInfo &info, TQString name)
  747. {
  748. UDSAtom atom;
  749. udsEntry.clear();
  750. atom.m_uds = UDS_FILE_TYPE; // UDS type
  751. atom.m_long = S_IFREG; // file
  752. udsEntry.append(atom);
  753. atom.m_uds = UDS_NAME;
  754. atom.m_str = name;
  755. udsEntry.append(atom);
  756. if (info.file.fields & GP_FILE_INFO_SIZE) {
  757. atom.m_uds = UDS_SIZE;
  758. atom.m_long = info.file.size;
  759. udsEntry.append(atom);
  760. }
  761. if (info.file.fields & GP_FILE_INFO_MTIME) {
  762. atom.m_uds = UDS_MODIFICATION_TIME;
  763. atom.m_long = info.file.mtime;
  764. udsEntry.append(atom);
  765. } else {
  766. atom.m_uds = UDS_MODIFICATION_TIME;
  767. atom.m_long = time(NULL); /* NOW */
  768. udsEntry.append(atom);
  769. }
  770. if (info.file.fields & GP_FILE_INFO_TYPE) {
  771. atom.m_uds = UDS_MIME_TYPE;
  772. atom.m_str = TQString::fromLatin1(info.file.type);
  773. udsEntry.append(atom);
  774. }
  775. if (info.file.fields & GP_FILE_INFO_PERMISSIONS) {
  776. atom.m_uds = UDS_ACCESS;
  777. atom.m_long = 0;
  778. atom.m_long |= (info.file.permissions & GP_FILE_PERM_READ) ? (S_IRUSR | S_IRGRP | S_IROTH) : 0;
  779. // we cannot represent individual FP_FILE_PERM_DELETE permission in the Unix access scheme
  780. // since the parent directory's write permission defines that
  781. udsEntry.append(atom);
  782. } else {
  783. // basic permissions, in case the camera doesn't provide permissions info
  784. atom.m_uds = UDS_ACCESS;
  785. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH;
  786. udsEntry.append(atom);
  787. }
  788. // TODO: We do not handle info.preview in any way
  789. }
  790. // translate a directory name to a UDSEntry which we can return as a directory listing entry
  791. void KameraProtocol::translateDirectoryToUDS(UDSEntry &udsEntry, const TQString &dirname)
  792. {
  793. UDSAtom atom;
  794. udsEntry.clear();
  795. atom.m_uds = UDS_FILE_TYPE; // UDS type
  796. atom.m_long = S_IFDIR; // directory
  797. udsEntry.append(atom);
  798. atom.m_uds = UDS_NAME;
  799. atom.m_str = dirname;
  800. udsEntry.append(atom);
  801. atom.m_uds = UDS_ACCESS;
  802. atom.m_long = S_IRUSR | S_IRGRP | S_IROTH |
  803. S_IWUSR | S_IWGRP | S_IWOTH;
  804. udsEntry.append(atom);
  805. atom.m_uds = UDS_MIME_TYPE;
  806. atom.m_str = "inode/directory";
  807. udsEntry.append(atom);
  808. }
  809. bool KameraProtocol::cameraSupportsDel(void)
  810. {
  811. return (m_abilities.file_operations & GP_FILE_OPERATION_DELETE);
  812. }
  813. bool KameraProtocol::cameraSupportsPut(void)
  814. {
  815. return (m_abilities.folder_operations & GP_FOLDER_OPERATION_PUT_FILE);
  816. }
  817. bool KameraProtocol::cameraSupportsPreview(void)
  818. {
  819. return (m_abilities.file_operations & GP_FILE_OPERATION_PREVIEW);
  820. }
  821. int KameraProtocol::readCameraFolder(const TQString &folder, CameraList *dirList, CameraList *fileList)
  822. {
  823. kdDebug(7123) << "KameraProtocol::readCameraFolder(" << folder << ")" << endl;
  824. int gpr;
  825. if((gpr = gp_camera_folder_list_folders(m_camera, tocstr(folder), dirList, m_context)) != GP_OK)
  826. return gpr;
  827. if((gpr = gp_camera_folder_list_files(m_camera, tocstr(folder), fileList, m_context)) != GP_OK)
  828. return gpr;
  829. return GP_OK;
  830. }
  831. void frontendProgressUpdate(
  832. GPContext * /*context*/, unsigned int /*id*/, float /*current*/, void *data
  833. ) {
  834. KameraProtocol *object = (KameraProtocol*)data;
  835. // This code will get the last chunk of data retrieved from the
  836. // camera and pass it to KIO, to allow progressive display
  837. // of the downloaded photo.
  838. const char *fileData = NULL;
  839. long unsigned int fileSize = 0;
  840. // This merely returns us a pointer to gphoto's internal data
  841. // buffer -- there's no expensive memcpy
  842. if (!object->getFile())
  843. return;
  844. gp_file_get_data_and_size(object->getFile(), &fileData, &fileSize);
  845. // make sure we're not sending zero-sized chunks (=EOF)
  846. if (fileSize > 0) {
  847. // XXX using assign() here causes segfault, prolly because
  848. // gp_file_free is called before chunkData goes out of scope
  849. TQByteArray chunkDataBuffer;
  850. chunkDataBuffer.setRawData(fileData + object->getFileSize(), fileSize - object->getFileSize());
  851. // Note: this will fail with sizes > 16MB ...
  852. object->data(chunkDataBuffer);
  853. object->processedSize(fileSize);
  854. chunkDataBuffer.resetRawData(fileData + object->getFileSize(), fileSize - object->getFileSize());
  855. object->setFileSize(fileSize);
  856. }
  857. }
  858. unsigned int frontendProgressStart(
  859. GPContext * /*context*/, float totalsize,
  860. #ifdef HAVE_GPHOTO2_5
  861. const char *status,
  862. #else
  863. const char *format, va_list args,
  864. #endif
  865. void *data
  866. ) {
  867. KameraProtocol *object = (KameraProtocol*)data;
  868. #ifndef HAVE_GPHOTO2_5
  869. char *status;
  870. /* We must copy the va_list to walk it twice, or all hell
  871. * breaks loose on non-i386 platforms.
  872. */
  873. #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY)
  874. va_list xvalist;
  875. # ifdef HAVE_VA_COPY
  876. va_copy(xvalist, args);
  877. # elif HAVE___VA_COPY
  878. __va_copy(xvalist, args);
  879. # endif
  880. int size=vsnprintf(NULL, 0, format, xvalist);
  881. if(size<=0)
  882. return GP_OK; // vsnprintf is broken, better don't do anything.
  883. status=new char[size+1];
  884. # ifdef HAVE_VA_COPY
  885. va_copy(xvalist, args);
  886. # elif HAVE___VA_COPY
  887. __va_copy(xvalist, args);
  888. # endif
  889. vsnprintf(status, size+1, format, xvalist);
  890. #else
  891. /* We cannot copy the va_list, so make sure we
  892. * walk it just _once_.
  893. */
  894. status=new char[300];
  895. vsnprintf(status, 300, format, args);
  896. #endif
  897. object->infoMessage(TQString::fromLocal8Bit(status));
  898. delete [] status;
  899. #else
  900. /* libgphoto2 2.5 has resolved this already, no need for print */
  901. object->infoMessage(TQString::fromLocal8Bit(status));
  902. #endif
  903. object->totalSize((int)totalsize); // hack: call slot directly
  904. return GP_OK;
  905. }
  906. // this callback function is activated on every status message from gphoto2
  907. static void frontendCameraStatus(
  908. GPContext * /*context*/,
  909. #ifdef HAVE_GPHOTO2_5
  910. const char *status,
  911. #else
  912. const char *format, va_list args,
  913. #endif
  914. void *data
  915. ) {
  916. KameraProtocol *object = (KameraProtocol*)data;
  917. #ifndef HAVE_GPHOTO2_5
  918. char *status;
  919. /* We must copy the va_list to walk it twice, or all hell
  920. * breaks loose on non-i386 platforms.
  921. */
  922. #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY)
  923. va_list xvalist;
  924. # ifdef HAVE_VA_COPY
  925. va_copy(xvalist, args);
  926. # elif HAVE___VA_COPY
  927. __va_copy(xvalist, args);
  928. # endif
  929. int size=vsnprintf(NULL, 0, format, xvalist);
  930. if(size<=0)
  931. return; // vsnprintf is broken, better don't do anything.
  932. status=new char[size+1];
  933. # ifdef HAVE_VA_COPY
  934. va_copy(xvalist, args);
  935. # elif HAVE___VA_COPY
  936. __va_copy(xvalist, args);
  937. # endif
  938. vsnprintf(status, size+1, format, xvalist);
  939. #else
  940. /* We cannot copy the va_list, so make sure we
  941. * walk it just _once_.
  942. */
  943. status=new char[300];
  944. vsnprintf(status, 300, format, args);
  945. #endif
  946. object->infoMessage(TQString::fromLocal8Bit(status));
  947. delete [] status;
  948. #else
  949. object->infoMessage(TQString::fromLocal8Bit(status));
  950. #endif
  951. }