there is no moc file generated for this class
[kdegraphics.git] / kamera / kioslave / kamera.cpp
blob22ee39e99473f398237ee8f16c8be9c5f3ab36f8
1 /*
3 Copyright (C) 2001 The Kompany
4 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il>
5 2001-2007 Marcus Meissner <marcus@jet.franken.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <signal.h>
30 #include <errno.h>
32 #include <qfile.h>
33 #include <qtextstream.h>
35 #include <kdebug.h>
36 #include <kcomponentdata.h>
37 #include <kstandarddirs.h>
38 #include <kconfig.h>
39 #include <kconfiggroup.h>
40 #include <klocale.h>
41 #include <kprotocolinfo.h>
42 #include <kio/global.h>
43 #include <kio/slaveconfig.h>
44 #include <kconfiggroup.h>
46 #include "config-kamera.h"
48 #include "kamera.h"
50 #define tocstr(x) ((x).toLocal8Bit())
52 #define MAXIDLETIME 30 /* seconds */
54 using namespace KIO;
56 extern "C"
58 KDE_EXPORT int kdemain(int argc, char **argv);
60 static void frontendCameraStatus(GPContext *context, const char *format, va_list args, void *data);
61 static unsigned int frontendProgressStart(
62 GPContext *context, float totalsize, const char *format,
63 va_list args, void *data
65 static void frontendProgressUpdate(
66 GPContext *context, unsigned int id, float current, void *data
70 int kdemain(int argc, char **argv)
72 KComponentData componentData("kio_kamera");
74 if(argc != 4) {
75 kDebug(7123) << "Usage: kio_kamera protocol "
76 "domain-socket1 domain-socket2" << endl;
77 exit(-1);
80 KameraProtocol slave(argv[2], argv[3]);
82 slave.dispatchLoop();
84 return 0;
87 KameraProtocol::KameraProtocol(const QByteArray &pool, const QByteArray &app)
88 : SlaveBase("camera", pool, app),
89 m_camera(NULL)
91 // attempt to initialize libgphoto2 and chosen camera (requires locking)
92 // (will init m_camera, since the m_camera's configuration is empty)
93 m_camera = 0;
94 m_file = NULL;
95 m_config = new KConfig(KProtocolInfo::config("camera"), KConfig::SimpleConfig);
96 m_context = gp_context_new();
97 actiondone = true;
98 cameraopen = false;
99 m_lockfile = KStandardDirs::locateLocal("tmp", "kamera");
100 idletime = 0;
103 // This handler is getting called every second. We use it to do the
104 // delayed close of the camera.
105 // Logic is:
106 // - No more requests in the queue (signaled by actiondone) AND
107 // - We are MAXIDLETIME seconds idle OR
108 // - Another slave wants to have access to the camera.
110 // The existence of a lockfile is used to signify "please give up camera".
112 void KameraProtocol::special(const QByteArray&) {
113 kDebug(7123) << "KameraProtocol::special() at " << getpid();
115 if (!actiondone && cameraopen) {
116 struct stat stbuf;
117 if ((-1!=::stat(m_lockfile.toUtf8(),&stbuf)) || (idletime++ >= MAXIDLETIME)) {
118 kDebug(7123) << "KameraProtocol::special() closing camera.";
119 closeCamera();
120 setTimeoutSpecialCommand(-1);
121 } else {
122 // continue to wait
123 setTimeoutSpecialCommand(1);
125 } else {
126 // We let it run until the slave gets no actions anymore.
127 setTimeoutSpecialCommand(1);
129 actiondone = false;
132 KameraProtocol::~KameraProtocol()
134 kDebug(7123) << "KameraProtocol::~KameraProtocol()";
135 delete m_config;
136 if(m_camera) {
137 closeCamera();
138 gp_camera_free(m_camera);
139 m_camera = NULL;
143 // initializes the camera for usage - should be done before operations over the wire
144 bool KameraProtocol::openCamera(QString &str) {
145 idletime = 0;
146 actiondone = true;
147 if (!m_camera) {
148 reparseConfiguration();
149 } else {
150 if (!cameraopen) {
151 int ret, tries = 15;
152 kDebug(7123) << "KameraProtocol::openCamera at " << getpid();
153 while (tries--) {
154 ret = gp_camera_init(m_camera, m_context);
155 if ( (ret == GP_ERROR_IO_USB_CLAIM) ||
156 (ret == GP_ERROR_IO_LOCK)) {
157 // just create / touch if not there
158 int fd = ::open(m_lockfile.toUtf8(),O_CREAT|O_WRONLY,0600);
159 if (fd != -1) ::close(fd);
160 ::sleep(1);
161 kDebug(7123) << "openCamera at " << getpid() << "- busy, ret " << ret << ", trying again.";
162 continue;
164 if (ret == GP_OK) break;
165 str = gp_result_as_string(ret);
166 return false;
168 ::unlink(m_lockfile.toUtf8());
169 setTimeoutSpecialCommand(1);
170 kDebug(7123) << "openCamera succeeded at " << getpid();
171 cameraopen = true;
174 return true;
177 // should be done after operations over the wire
178 void KameraProtocol::closeCamera(void)
180 int gpr;
182 if (!m_camera)
183 return;
185 kDebug(7123) << "KameraProtocol::closeCamera at " << getpid();
186 if ((gpr=gp_camera_exit(m_camera,m_context))!=GP_OK) {
187 kDebug(7123) << "closeCamera failed with " << gp_result_as_string(gpr);
189 // HACK: gp_camera_exit() in gp 2.0 does not close the port if there
190 // is no camera_exit function.
191 gp_port_close(m_camera->port);
192 cameraopen = false;
193 return;
196 static QString fix_foldername(QString ofolder) {
197 QString folder = ofolder;
198 if (folder.length() > 1) {
199 while ((folder.length()>1) && (folder.right(1) == "/"))
200 folder = folder.left(folder.length()-1);
202 if (folder.length() == 0)
203 folder = "/";
204 return folder;
207 // The KIO slave "get" function (starts a download from the camera)
208 // The actual returning of the data is done in the frontend callback functions.
209 void KameraProtocol::get(const KUrl &url)
211 kDebug(7123) << "KameraProtocol::get(" << url.path() << ")";
213 CameraFileType fileType;
214 int gpr;
215 if (url.host().isEmpty()) {
216 error(KIO::ERR_DOES_NOT_EXIST, url.path());
217 return;
220 if(!openCamera()) {
221 error(KIO::ERR_DOES_NOT_EXIST, url.path());
222 return;
225 // fprintf(stderr,"get(%s)\n",url.path().toLatin1());
227 #define GPHOTO_TEXT_FILE(xx) \
228 if (!url.path().compare("/" #xx ".txt")) { \
229 CameraText xx; \
230 gpr = gp_camera_get_##xx(m_camera, &xx, m_context); \
231 if (gpr != GP_OK) { \
232 error(KIO::ERR_DOES_NOT_EXIST, url.path()); \
233 return; \
235 QByteArray chunkDataBuffer = QByteArray::fromRawData(xx.text, strlen(xx.text)); \
236 data(chunkDataBuffer); \
237 processedSize(strlen(xx.text)); \
238 chunkDataBuffer.clear(); \
239 finished(); \
240 return; \
243 GPHOTO_TEXT_FILE(about);
244 GPHOTO_TEXT_FILE(manual);
245 GPHOTO_TEXT_FILE(summary);
247 #undef GPHOTO_TEXT_FILE
248 // emit info message
249 infoMessage( i18n("Retrieving data from camera <b>%1</b>", url.user()) );
251 // Note: There's no need to re-read directory for each get() anymore
252 gp_file_new(&m_file);
254 // emit the total size (we must do it before sending data to allow preview)
255 CameraFileInfo info;
257 gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), tocstr(url.fileName()), &info, m_context);
258 if (gpr != GP_OK) {
259 // fprintf(stderr,"Folder %s / File %s not found, gpr is %d\n",folder.toLatin1(), url.fileName().toLatin1(), gpr);
260 gp_file_unref(m_file);
261 if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
262 error(KIO::ERR_DOES_NOT_EXIST, url.path());
263 else
264 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
265 return;
268 // at last, a proper API to determine whether a thumbnail was requested.
269 if(cameraSupportsPreview() && metaData("thumbnail") == "1") {
270 kDebug(7123) << "get() retrieving the thumbnail";
271 fileType = GP_FILE_TYPE_PREVIEW;
272 if (info.preview.fields & GP_FILE_INFO_SIZE)
273 totalSize(info.preview.size);
274 if (info.preview.fields & GP_FILE_INFO_TYPE)
275 mimeType(info.preview.type);
276 } else {
277 kDebug(7123) << "get() retrieving the full-scale photo";
278 fileType = GP_FILE_TYPE_NORMAL;
279 if (info.file.fields & GP_FILE_INFO_SIZE)
280 totalSize(info.file.size);
281 if (info.preview.fields & GP_FILE_INFO_TYPE)
282 mimeType(info.file.type);
285 // fetch the data
286 m_fileSize = 0;
287 gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), tocstr(url.fileName()), fileType, m_file, m_context);
288 if ( (gpr == GP_ERROR_NOT_SUPPORTED) &&
289 (fileType == GP_FILE_TYPE_PREVIEW)
291 // If we get here, the file info command information
292 // will either not be used, or still valid.
293 fileType = GP_FILE_TYPE_NORMAL;
294 gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), tocstr(url.fileName()), fileType, m_file, m_context);
296 switch(gpr) {
297 case GP_OK:
298 break;
299 case GP_ERROR_FILE_NOT_FOUND:
300 case GP_ERROR_DIRECTORY_NOT_FOUND:
301 gp_file_unref(m_file);
302 m_file = NULL;
303 error(KIO::ERR_DOES_NOT_EXIST, url.fileName());
304 return ;
305 default:
306 gp_file_unref(m_file);
307 m_file = NULL;
308 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
309 return;
311 // emit the mimetype
312 // NOTE: we must first get the file, so that CameraFile->name would be set
313 const char *fileMimeType;
314 gp_file_get_mime_type(m_file, &fileMimeType);
315 mimeType(fileMimeType);
317 // We need to pass left over data here. Some camera drivers do not
318 // implement progress callbacks!
319 const char *fileData;
320 long unsigned int fileSize;
321 // This merely returns us a pointer to gphoto's internal data
322 // buffer -- there's no expensive memcpy
323 gpr = gp_file_get_data_and_size(m_file, &fileData, &fileSize);
324 if (gpr != GP_OK) {
325 kDebug(7123) << "get():: get_data_and_size failed.";
326 gp_file_free(m_file);
327 m_file = NULL;
328 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
329 return;
331 // make sure we're not sending zero-sized chunks (=EOF)
332 // also make sure we send only if the progress did not send the data
333 // already.
334 if ((fileSize > 0) && (fileSize - m_fileSize)>0) {
335 unsigned long written = 0;
336 QByteArray chunkDataBuffer;
338 // We need to split it up here. Someone considered it funny
339 // to discard any data() larger than 16MB.
341 // So nearly any Movie will just fail....
342 while (written < fileSize-m_fileSize) {
343 unsigned long towrite = 1024*1024; // 1MB
345 if (towrite > fileSize-m_fileSize-written)
346 towrite = fileSize-m_fileSize-written;
347 chunkDataBuffer = QByteArray::fromRawData(fileData + m_fileSize + written, towrite);
348 processedSize(m_fileSize + written + towrite);
349 data(chunkDataBuffer);
350 chunkDataBuffer.clear();
351 written += towrite;
353 m_fileSize = fileSize;
354 setFileSize(fileSize);
357 finished();
358 gp_file_unref(m_file); /* just unref, might be stored in fs */
359 m_file = NULL;
362 // The KIO slave "stat" function.
363 void KameraProtocol::stat(const KUrl &url)
365 kDebug(7123) << "stat(\"" << url.path() << "\")";
367 if (url.path().isEmpty()) {
368 KUrl rooturl(url);
370 kDebug(7123) << "redirecting to /";
371 rooturl.setPath("/");
372 rooturl.setHost(url.host());
373 rooturl.setUser(url.user());
374 redirection(rooturl);
375 finished();
376 return;
379 if(url.path() == "/")
380 statRoot();
381 else
382 statRegular(url);
385 // Implements stat("/") -- which always returns the same value.
386 void KameraProtocol::statRoot(void)
388 KIO::UDSEntry entry;
390 entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLocal8Bit("/"));
392 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFDIR);
394 entry.insert(KIO::UDSEntry::UDS_ACCESS,(S_IRUSR | S_IRGRP | S_IROTH |S_IWUSR | S_IWGRP | S_IWOTH));
396 statEntry(entry);
398 finished();
399 // If we just do this call, timeout right away if no other requests are
400 // pending. This is for the kdemm autodetection using media://camera
401 idletime = MAXIDLETIME;
404 // Implements a regular stat() of a file / directory, returning all we know about it
405 void KameraProtocol::statRegular(const KUrl &url)
407 KIO::UDSEntry entry;
408 int gpr;
410 kDebug(7123) << "statRegular(\"" << url.path() << "\")";
411 if (openCamera() == false) {
412 error(KIO::ERR_DOES_NOT_EXIST, url.path());
413 return;
416 // fprintf(stderr,"statRegular(%s)\n",url.path().toLatin1());
418 // Is "url" a directory?
419 CameraList *dirList;
420 gp_list_new(&dirList);
421 kDebug(7123) << "statRegular() Requesting directories list for " << url.directory();
423 gpr = gp_camera_folder_list_folders(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), dirList, m_context);
424 if (gpr != GP_OK) {
425 if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
426 error(KIO::ERR_DOES_NOT_EXIST, url.path());
427 else
428 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
429 gp_list_free(dirList);
430 return;
433 #define GPHOTO_TEXT_FILE(xx) \
434 if (!url.path().compare("/"#xx".txt")) { \
435 CameraText xx; \
436 gpr = gp_camera_get_about(m_camera, &xx, m_context); \
437 if (gpr != GP_OK) { \
438 error(KIO::ERR_DOES_NOT_EXIST, url.fileName()); \
439 return; \
441 translateTextToUDS(entry,#xx".txt",xx.text); \
442 statEntry(entry); \
443 finished(); \
444 return; \
446 GPHOTO_TEXT_FILE(about);
447 GPHOTO_TEXT_FILE(manual);
448 GPHOTO_TEXT_FILE(summary);
449 #undef GPHOTO_TEXT_FILE
451 const char *name;
452 for(int i = 0; i < gp_list_count(dirList); i++) {
453 gp_list_get_name(dirList, i, &name);
454 if (url.fileName().compare(name) == 0) {
455 gp_list_free(dirList);
456 KIO::UDSEntry entry;
457 translateDirectoryToUDS(entry, url.fileName());
458 statEntry(entry);
459 finished();
460 return;
463 gp_list_free(dirList);
465 // Is "url" a file?
466 CameraFileInfo info;
467 gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), tocstr(url.fileName()), &info, m_context);
468 if (gpr != GP_OK) {
469 if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND))
470 error(KIO::ERR_DOES_NOT_EXIST, url.path());
471 else
472 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
473 return;
475 translateFileToUDS(entry, info, url.fileName());
476 statEntry(entry);
477 finished();
480 // The KIO slave "del" function.
481 void KameraProtocol::del(const KUrl &url, bool isFile)
483 kDebug(7123) << "KameraProtocol::del(" << url.path() << ")";
485 if(!openCamera()) {
486 error(KIO::ERR_CANNOT_DELETE, url.fileName());
487 return;
489 if (!cameraSupportsDel()) {
490 error(KIO::ERR_CANNOT_DELETE, url.fileName());
491 return;
493 if(isFile){
494 CameraList *list;
495 gp_list_new(&list);
496 int ret;
498 ret = gp_camera_file_delete(m_camera, tocstr(fix_foldername(url.directory(KUrl::AppendTrailingSlash))), tocstr(url.fileName()), m_context);
500 if(ret != GP_OK) {
501 error(KIO::ERR_CANNOT_DELETE, url.fileName());
502 } else {
503 finished();
508 // The KIO slave "listDir" function.
509 void KameraProtocol::listDir(const KUrl &url)
511 kDebug(7123) << "KameraProtocol::listDir(" << url.path() << ")";
513 if (url.host().isEmpty()) {
514 KUrl xurl;
515 // List the available cameras
516 QStringList groupList = m_config->groupList();
517 kDebug(7123) << "Found cameras: " << groupList.join(", ");
518 QStringList::Iterator it;
519 KIO::UDSEntry entry;
523 * What we do:
524 * - Autodetect cameras and remember them with their ports.
525 * - List all saved and possible offline cameras.
526 * - List all autodetected and not yet printed cameras.
528 QMap<QString,QString> ports, names;
529 QMap<QString,int> modelcnt;
531 /* Autodetect USB cameras ... */
532 GPContext *glob_context = NULL;
533 int i, count;
534 CameraList *list;
535 CameraAbilitiesList *al;
536 GPPortInfoList *il;
538 gp_list_new (&list);
539 gp_abilities_list_new (&al);
540 gp_abilities_list_load (al, glob_context);
541 gp_port_info_list_new (&il);
542 gp_port_info_list_load (il);
543 gp_abilities_list_detect (al, il, list, glob_context);
544 gp_abilities_list_free (al);
545 gp_port_info_list_free (il);
547 count = gp_list_count (list);
549 for (i = 0 ; i<count ; i++) {
550 const char *model, *value;
552 gp_list_get_name (list, i, &model);
553 gp_list_get_value (list, i, &value);
555 ports[value] = model;
556 // NOTE: We might get different ports than usb: later!
557 if (strcmp(value,"usb:") != 0)
558 names[model] = value;
560 /* Save them, even though we can autodetect them for
561 * offline listing.
563 KConfigGroup cg(m_config, model);
564 cg.writeEntry("Model", model);
565 cg.writeEntry("Path", value);
566 modelcnt[model]++;
568 gp_list_free (list);
570 /* Avoid duplicated entry, that is a camera with both port usb: and usb:001,042 entries. */
571 if (ports.contains("usb:") && names.contains(ports["usb:"]) && names[ports["usb:"]] != "usb:")
572 ports.remove("usb:");
574 for (it = groupList.begin(); it != groupList.end(); it++) {
575 QString m_cfgPath;
576 if (*it == "<default>")
577 continue;
579 KConfigGroup cg(m_config, *it);
580 m_cfgPath = cg.readEntry("Path");
582 /* If autodetect by USB autodetect ... skip it here.
583 * We leave unattached USB cameras in here, because the user
584 * might plug them in later and does not want to press reload.
585 * We add them with port "usb:".
587 if (modelcnt[*it] > 0)
588 continue;
590 entry.clear();
591 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFDIR);
592 entry.insert(KIO::UDSEntry::UDS_NAME,*it);
593 entry.insert(KIO::UDSEntry::UDS_ACCESS,(S_IRUSR | S_IRGRP | S_IROTH |S_IWUSR | S_IWGRP | S_IWOTH));
596 xurl.setProtocol("camera");
597 xurl.setUser(*it);
598 /* Avoid setting usb:xxx,yyy. */
599 if (m_cfgPath.contains("usb:")>0) {
600 names[*it] = "usb:";
601 xurl.setHost("usb:");
602 } else {
603 xurl.setHost(m_cfgPath);
605 xurl.setPath("/");
606 xurl.setUrl(xurl.url().replace(":", "___"));
607 entry.insert(KIO::UDSEntry::UDS_TARGET_URL,xurl.url());
609 listEntry(entry, false);
612 QMap<QString,QString>::iterator portsit;
614 for (portsit = ports.begin(); portsit != ports.end(); portsit++) {
615 entry.clear();
616 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFDIR);
617 entry.insert(KIO::UDSEntry::UDS_NAME, portsit.value());
619 entry.insert(KIO::UDSEntry::UDS_ACCESS,(S_IRUSR | S_IRGRP | S_IROTH |S_IWUSR | S_IWGRP | S_IWOTH));
621 xurl.setProtocol("camera");
622 xurl.setHost(portsit.key());
623 xurl.setUser(portsit.value());
624 xurl.setPath("/");
625 xurl.setHost(xurl.host().replace(":", "___"));
626 entry.insert(KIO::UDSEntry::UDS_TARGET_URL,xurl.url());
628 listEntry(entry, false);
630 listEntry(entry, true);
632 finished();
633 return;
636 if (url.path().isEmpty()) {
637 KUrl rooturl(url);
639 kDebug(7123) << "redirecting to /";
640 rooturl.setPath("/");
641 rooturl.setHost(url.host());
642 rooturl.setUser(url.user());
643 redirection(rooturl);
644 finished();
645 return;
648 if (!openCamera()) {
649 error(KIO::ERR_COULD_NOT_READ,url.path());
650 return;
653 CameraList *dirList;
654 CameraList *fileList;
655 CameraList *specialList;
656 gp_list_new(&dirList);
657 gp_list_new(&fileList);
658 gp_list_new(&specialList);
659 int gpr;
661 if (!url.path().compare("/")) {
662 CameraText text;
663 if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context))
664 gp_list_append(specialList,"manual.txt",NULL);
665 if (GP_OK == gp_camera_get_about(m_camera, &text, m_context))
666 gp_list_append(specialList,"about.txt",NULL);
667 if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context))
668 gp_list_append(specialList,"summary.txt",NULL);
671 gpr = readCameraFolder(url.path(), dirList, fileList);
672 if(gpr != GP_OK) {
673 kDebug(7123) << "read Camera Folder failed:" << gp_result_as_string(gpr);
674 gp_list_free(dirList);
675 gp_list_free(fileList);
676 gp_list_free(specialList);
677 error(KIO::ERR_COULD_NOT_READ, gp_result_as_string(gpr));
678 return;
681 totalSize(gp_list_count(specialList) + gp_list_count(dirList) + gp_list_count(fileList));
683 KIO::UDSEntry entry;
684 const char *name;
686 for(int i = 0; i < gp_list_count(dirList); ++i) {
687 gp_list_get_name(dirList, i, &name);
688 translateDirectoryToUDS(entry, QString::fromLocal8Bit(name));
689 listEntry(entry, false);
692 CameraFileInfo info;
694 for(int i = 0; i < gp_list_count(fileList); ++i) {
695 gp_list_get_name(fileList, i, &name);
696 // we want to know more info about files (size, type...)
697 gp_camera_file_get_info(m_camera, tocstr(url.path()), name, &info, m_context);
698 translateFileToUDS(entry, info, QString::fromLocal8Bit(name));
699 listEntry(entry, false);
701 if (!url.path().compare("/")) {
702 CameraText text;
703 if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) {
704 translateTextToUDS(entry, "manual.txt", text.text);
705 listEntry(entry, false);
707 if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) {
708 translateTextToUDS(entry, "about.txt", text.text);
709 listEntry(entry, false);
711 if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) {
712 translateTextToUDS(entry, "summary.txt", text.text);
713 listEntry(entry, false);
718 gp_list_free(fileList);
719 gp_list_free(dirList);
720 gp_list_free(specialList);
722 listEntry(entry, true); // 'entry' is not used in this case - we only signal list completion
723 finished();
726 void KameraProtocol::setHost(const QString& host, quint16 port, const QString& user, const QString& pass )
728 QString theHost = host;
729 theHost.replace("___", ":");
730 kDebug(7123) << "KameraProtocol::setHost(" << theHost << ", " << port << ", " << user << ", " << pass << ")";
731 int gpr, idx;
733 if (!theHost.isEmpty()) {
734 kDebug(7123) << "model is " << user << ", port is " << theHost;
735 if (m_camera) {
736 kDebug(7123) << "Configuration change detected";
737 closeCamera();
738 gp_camera_unref(m_camera);
739 m_camera = NULL;
740 infoMessage( i18n("Reinitializing camera") );
741 } else {
742 kDebug(7123) << "Initializing camera";
743 infoMessage( i18n("Initializing camera") );
745 // fetch abilities
746 CameraAbilitiesList *abilities_list;
747 gp_abilities_list_new(&abilities_list);
748 gp_abilities_list_load(abilities_list, m_context);
749 idx = gp_abilities_list_lookup_model(abilities_list, tocstr(user));
750 if (idx < 0) {
751 gp_abilities_list_free(abilities_list);
752 kDebug(7123) << "Unable to get abilities for model: " << user;
753 error(KIO::ERR_UNKNOWN, gp_result_as_string(idx));
754 return;
756 gp_abilities_list_get_abilities(abilities_list, idx, &m_abilities);
757 gp_abilities_list_free(abilities_list);
759 // fetch port
760 GPPortInfoList *port_info_list;
761 GPPortInfo port_info;
762 gp_port_info_list_new(&port_info_list);
763 gp_port_info_list_load(port_info_list);
764 idx = gp_port_info_list_lookup_path(port_info_list, tocstr(theHost));
766 /* Handle erronously passed usb:XXX,YYY */
767 if ((idx < 0) && theHost.startsWith("usb:"))
768 idx = gp_port_info_list_lookup_path(port_info_list, "usb:");
769 if (idx < 0) {
770 gp_port_info_list_free(port_info_list);
771 kDebug(7123) << "Unable to get port info for path: " << theHost;
772 error(KIO::ERR_UNKNOWN, gp_result_as_string(idx));
773 return;
775 gp_port_info_list_get_info(port_info_list, idx, &port_info);
776 gp_port_info_list_free(port_info_list);
778 // create a new camera object
779 gpr = gp_camera_new(&m_camera);
780 if(gpr != GP_OK) {
781 error(KIO::ERR_UNKNOWN, gp_result_as_string(gpr));
782 return;
785 // register gphoto2 callback functions
786 gp_context_set_status_func(m_context, frontendCameraStatus, this);
787 gp_context_set_progress_funcs(m_context, frontendProgressStart, frontendProgressUpdate, NULL, this);
788 // gp_camera_set_message_func(m_camera, ..., this)
790 // set model and port
791 gp_camera_set_abilities(m_camera, m_abilities);
792 gp_camera_set_port_info(m_camera, port_info);
793 gp_camera_set_port_speed(m_camera, 0); // TODO: the value needs to be configurable
794 kDebug(7123) << "Opening camera model " << user << " at " << theHost;
796 QString errstr;
797 if (!openCamera(errstr)) {
798 if (m_camera)
799 gp_camera_unref(m_camera);
800 m_camera = NULL;
801 kDebug(7123) << "Unable to init camera: " << errstr;
802 error(KIO::ERR_SERVICE_NOT_AVAILABLE, errstr);
803 return;
808 void KameraProtocol::reparseConfiguration(void)
810 // we have no global config, do we?
813 // translate a simple text to a UDS entry
814 void KameraProtocol::translateTextToUDS(KIO::UDSEntry &udsEntry, const QString &fn,
815 const char *text
818 udsEntry.clear();
820 udsEntry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFREG);
822 udsEntry.insert(KIO::UDSEntry::UDS_NAME,fn);
824 udsEntry.insert(KIO::UDSEntry::UDS_SIZE,strlen(text));
826 udsEntry.insert(KIO::UDSEntry::UDS_ACCESS,(S_IRUSR | S_IRGRP | S_IROTH));
829 // translate a CameraFileInfo to a UDSFieldType which we can return as a directory listing entry
830 void KameraProtocol::translateFileToUDS(KIO::UDSEntry &udsEntry, const CameraFileInfo &info, QString name)
833 udsEntry.clear();
835 udsEntry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFREG);
837 if (info.file.fields & GP_FILE_INFO_NAME)
838 udsEntry.insert(KIO::UDSEntry::UDS_NAME,QString::fromLocal8Bit(info.file.name));
839 else
840 udsEntry.insert(KIO::UDSEntry::UDS_NAME,name);
842 if (info.file.fields & GP_FILE_INFO_SIZE) {
843 udsEntry.insert(KIO::UDSEntry::UDS_SIZE,info.file.size);
846 if (info.file.fields & GP_FILE_INFO_MTIME) {
847 udsEntry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME,info.file.mtime);
848 } else {
849 udsEntry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME,time(NULL));
852 if (info.file.fields & GP_FILE_INFO_TYPE) {
853 udsEntry.insert(KIO::UDSEntry::UDS_MIME_TYPE,QString::fromLatin1(info.file.type));
856 if (info.file.fields & GP_FILE_INFO_PERMISSIONS) {
857 udsEntry.insert(KIO::UDSEntry::UDS_ACCESS,((info.file.permissions & GP_FILE_PERM_READ) ? (S_IRUSR | S_IRGRP | S_IROTH) : 0));
858 } else {
859 udsEntry.insert(KIO::UDSEntry::UDS_ACCESS,S_IRUSR | S_IRGRP | S_IROTH);
862 // TODO: We do not handle info.preview in any way
865 // translate a directory name to a UDSFieldType which we can return as a directory listing entry
866 void KameraProtocol::translateDirectoryToUDS(KIO::UDSEntry &udsEntry, const QString &dirname)
869 udsEntry.clear();
871 udsEntry.insert(KIO::UDSEntry::UDS_FILE_TYPE,S_IFDIR);
872 udsEntry.insert(KIO::UDSEntry::UDS_NAME,dirname);
873 udsEntry.insert(KIO::UDSEntry::UDS_ACCESS,S_IRUSR | S_IRGRP | S_IROTH |S_IWUSR | S_IWGRP | S_IWOTH);
874 udsEntry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString("inode/directory"));
877 bool KameraProtocol::cameraSupportsDel(void)
879 return (m_abilities.file_operations & GP_FILE_OPERATION_DELETE);
882 bool KameraProtocol::cameraSupportsPut(void)
884 return (m_abilities.folder_operations & GP_FOLDER_OPERATION_PUT_FILE);
887 bool KameraProtocol::cameraSupportsPreview(void)
889 return (m_abilities.file_operations & GP_FILE_OPERATION_PREVIEW);
892 int KameraProtocol::readCameraFolder(const QString &folder, CameraList *dirList, CameraList *fileList)
894 kDebug(7123) << "KameraProtocol::readCameraFolder(" << folder << ")";
896 int gpr;
897 if((gpr = gp_camera_folder_list_folders(m_camera, tocstr(folder), dirList, m_context)) != GP_OK)
898 return gpr;
899 if((gpr = gp_camera_folder_list_files(m_camera, tocstr(folder), fileList, m_context)) != GP_OK)
900 return gpr;
901 return GP_OK;
904 void frontendProgressUpdate(
905 GPContext * /*context*/, unsigned int /*id*/, float /*current*/, void *data
907 KameraProtocol *object = (KameraProtocol*)data;
909 // This code will get the last chunk of data retrieved from the
910 // camera and pass it to KIO, to allow progressive display
911 // of the downloaded photo.
913 const char *fileData = NULL;
914 long unsigned int fileSize = 0;
916 // This merely returns us a pointer to gphoto's internal data
917 // buffer -- there's no expensive memcpy
918 if (!object->getFile())
919 return;
920 gp_file_get_data_and_size(object->getFile(), &fileData, &fileSize);
921 // make sure we're not sending zero-sized chunks (=EOF)
922 if (fileSize > 0) {
923 // XXX using assign() here causes segfault, prolly because
924 // gp_file_free is called before chunkData goes out of scope
925 QByteArray chunkDataBuffer = QByteArray::fromRawData(fileData + object->getFileSize(), fileSize - object->getFileSize());
926 // Note: this will fail with sizes > 16MB ...
927 object->data(chunkDataBuffer);
928 object->processedSize(fileSize);
929 chunkDataBuffer.clear();
930 object->setFileSize(fileSize);
934 unsigned int frontendProgressStart(
935 GPContext * /*context*/, float totalsize, const char *format, va_list args,
936 void *data
938 KameraProtocol *object = (KameraProtocol*)data;
939 char *status;
941 /* We must copy the va_list to walk it twice, or all hell
942 * breaks loose on non-i386 platforms.
944 #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY)
945 va_list xvalist;
946 # ifdef HAVE_VA_COPY
947 va_copy(xvalist, args);
948 # elif HAVE___VA_COPY
949 __va_copy(xvalist, args);
950 # endif
951 int size=vsnprintf(NULL, 0, format, xvalist);
952 if(size<=0)
953 return GP_OK; // vsnprintf is broken, better don't do anything.
955 status=new char[size+1];
956 # ifdef HAVE_VA_COPY
957 va_copy(xvalist, args);
958 # elif HAVE___VA_COPY
959 __va_copy(xvalist, args);
960 # endif
961 vsnprintf(status, size+1, format, xvalist);
962 #else
963 /* We cannot copy the va_list, so make sure we
964 * walk it just _once_.
966 status=new char[300];
967 vsnprintf(status, 300, format, args);
968 #endif
970 object->infoMessage(QString::fromLocal8Bit(status));
971 delete [] status;
972 object->totalSize((int)totalsize); // hack: call slot directly
973 return GP_OK;
976 // this callback function is activated on every status message from gphoto2
977 static void frontendCameraStatus(GPContext * /*context*/, const char *format, va_list args, void *data)
979 KameraProtocol *object = (KameraProtocol*)data;
980 char *status;
982 /* We must copy the va_list to walk it twice, or all hell
983 * breaks loose on non-i386 platforms.
985 #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY)
986 va_list xvalist;
987 # ifdef HAVE_VA_COPY
988 va_copy(xvalist, args);
989 # elif HAVE___VA_COPY
990 __va_copy(xvalist, args);
991 # endif
992 int size=vsnprintf(NULL, 0, format, xvalist);
993 if(size<=0)
994 return; // vsnprintf is broken, better don't do anything.
996 status=new char[size+1];
997 # ifdef HAVE_VA_COPY
998 va_copy(xvalist, args);
999 # elif HAVE___VA_COPY
1000 __va_copy(xvalist, args);
1001 # endif
1002 vsnprintf(status, size+1, format, xvalist);
1003 #else
1004 /* We cannot copy the va_list, so make sure we
1005 * walk it just _once_.
1007 status=new char[300];
1008 vsnprintf(status, 300, format, args);
1009 #endif
1010 object->infoMessage(QString::fromLocal8Bit(status));
1011 delete [] status;