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.
28 #include <sys/types.h>
33 #include <qtextstream.h>
36 #include <kcomponentdata.h>
37 #include <kstandarddirs.h>
39 #include <kconfiggroup.h>
41 #include <kprotocolinfo.h>
42 #include <kio/global.h>
43 #include <kio/slaveconfig.h>
44 #include <kconfiggroup.h>
46 #include "config-kamera.h"
50 #define tocstr(x) ((x).toLocal8Bit())
52 #define MAXIDLETIME 30 /* seconds */
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");
75 kDebug(7123) << "Usage: kio_kamera protocol "
76 "domain-socket1 domain-socket2" << endl
;
80 KameraProtocol
slave(argv
[2], argv
[3]);
87 KameraProtocol::KameraProtocol(const QByteArray
&pool
, const QByteArray
&app
)
88 : SlaveBase("camera", pool
, app
),
91 // attempt to initialize libgphoto2 and chosen camera (requires locking)
92 // (will init m_camera, since the m_camera's configuration is empty)
95 m_config
= new KConfig(KProtocolInfo::config("camera"), KConfig::SimpleConfig
);
96 m_context
= gp_context_new();
99 m_lockfile
= KStandardDirs::locateLocal("tmp", "kamera");
103 // This handler is getting called every second. We use it to do the
104 // delayed close of the camera.
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
) {
117 if ((-1!=::stat(m_lockfile
.toUtf8(),&stbuf
)) || (idletime
++ >= MAXIDLETIME
)) {
118 kDebug(7123) << "KameraProtocol::special() closing camera.";
120 setTimeoutSpecialCommand(-1);
123 setTimeoutSpecialCommand(1);
126 // We let it run until the slave gets no actions anymore.
127 setTimeoutSpecialCommand(1);
132 KameraProtocol::~KameraProtocol()
134 kDebug(7123) << "KameraProtocol::~KameraProtocol()";
138 gp_camera_free(m_camera
);
143 // initializes the camera for usage - should be done before operations over the wire
144 bool KameraProtocol::openCamera(QString
&str
) {
148 reparseConfiguration();
152 kDebug(7123) << "KameraProtocol::openCamera at " << getpid();
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
);
161 kDebug(7123) << "openCamera at " << getpid() << "- busy, ret " << ret
<< ", trying again.";
164 if (ret
== GP_OK
) break;
165 str
= gp_result_as_string(ret
);
168 ::unlink(m_lockfile
.toUtf8());
169 setTimeoutSpecialCommand(1);
170 kDebug(7123) << "openCamera succeeded at " << getpid();
177 // should be done after operations over the wire
178 void KameraProtocol::closeCamera(void)
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
);
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)
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
;
215 if (url
.host().isEmpty()) {
216 error(KIO::ERR_DOES_NOT_EXIST
, url
.path());
221 error(KIO::ERR_DOES_NOT_EXIST
, url
.path());
225 // fprintf(stderr,"get(%s)\n",url.path().toLatin1());
227 #define GPHOTO_TEXT_FILE(xx) \
228 if (!url.path().compare("/" #xx ".txt")) { \
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()); \
235 QByteArray chunkDataBuffer = QByteArray::fromRawData(xx.text, strlen(xx.text)); \
236 data(chunkDataBuffer); \
237 processedSize(strlen(xx.text)); \
238 chunkDataBuffer.clear(); \
243 GPHOTO_TEXT_FILE(about
);
244 GPHOTO_TEXT_FILE(manual
);
245 GPHOTO_TEXT_FILE(summary
);
247 #undef GPHOTO_TEXT_FILE
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)
257 gpr
= gp_camera_file_get_info(m_camera
, tocstr(fix_foldername(url
.directory(KUrl::AppendTrailingSlash
))), tocstr(url
.fileName()), &info
, m_context
);
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());
264 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
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
);
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
);
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
);
299 case GP_ERROR_FILE_NOT_FOUND
:
300 case GP_ERROR_DIRECTORY_NOT_FOUND
:
301 gp_file_unref(m_file
);
303 error(KIO::ERR_DOES_NOT_EXIST
, url
.fileName());
306 gp_file_unref(m_file
);
308 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
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
);
325 kDebug(7123) << "get():: get_data_and_size failed.";
326 gp_file_free(m_file
);
328 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
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
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();
353 m_fileSize
= fileSize
;
354 setFileSize(fileSize
);
358 gp_file_unref(m_file
); /* just unref, might be stored in fs */
362 // The KIO slave "stat" function.
363 void KameraProtocol::stat(const KUrl
&url
)
365 kDebug(7123) << "stat(\"" << url
.path() << "\")";
367 if (url
.path().isEmpty()) {
370 kDebug(7123) << "redirecting to /";
371 rooturl
.setPath("/");
372 rooturl
.setHost(url
.host());
373 rooturl
.setUser(url
.user());
374 redirection(rooturl
);
379 if(url
.path() == "/")
385 // Implements stat("/") -- which always returns the same value.
386 void KameraProtocol::statRoot(void)
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
));
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
)
410 kDebug(7123) << "statRegular(\"" << url
.path() << "\")";
411 if (openCamera() == false) {
412 error(KIO::ERR_DOES_NOT_EXIST
, url
.path());
416 // fprintf(stderr,"statRegular(%s)\n",url.path().toLatin1());
418 // Is "url" a directory?
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
);
425 if ((gpr
== GP_ERROR_FILE_NOT_FOUND
) || (gpr
== GP_ERROR_DIRECTORY_NOT_FOUND
))
426 error(KIO::ERR_DOES_NOT_EXIST
, url
.path());
428 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
429 gp_list_free(dirList
);
433 #define GPHOTO_TEXT_FILE(xx) \
434 if (!url.path().compare("/"#xx".txt")) { \
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()); \
441 translateTextToUDS(entry,#xx".txt",xx.text); \
446 GPHOTO_TEXT_FILE(about
);
447 GPHOTO_TEXT_FILE(manual
);
448 GPHOTO_TEXT_FILE(summary
);
449 #undef GPHOTO_TEXT_FILE
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
);
457 translateDirectoryToUDS(entry
, url
.fileName());
463 gp_list_free(dirList
);
467 gpr
= gp_camera_file_get_info(m_camera
, tocstr(fix_foldername(url
.directory(KUrl::AppendTrailingSlash
))), tocstr(url
.fileName()), &info
, m_context
);
469 if ((gpr
== GP_ERROR_FILE_NOT_FOUND
) || (gpr
== GP_ERROR_DIRECTORY_NOT_FOUND
))
470 error(KIO::ERR_DOES_NOT_EXIST
, url
.path());
472 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
475 translateFileToUDS(entry
, info
, url
.fileName());
480 // The KIO slave "del" function.
481 void KameraProtocol::del(const KUrl
&url
, bool isFile
)
483 kDebug(7123) << "KameraProtocol::del(" << url
.path() << ")";
486 error(KIO::ERR_CANNOT_DELETE
, url
.fileName());
489 if (!cameraSupportsDel()) {
490 error(KIO::ERR_CANNOT_DELETE
, url
.fileName());
498 ret
= gp_camera_file_delete(m_camera
, tocstr(fix_foldername(url
.directory(KUrl::AppendTrailingSlash
))), tocstr(url
.fileName()), m_context
);
501 error(KIO::ERR_CANNOT_DELETE
, url
.fileName());
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()) {
515 // List the available cameras
516 QStringList groupList
= m_config
->groupList();
517 kDebug(7123) << "Found cameras: " << groupList
.join(", ");
518 QStringList::Iterator it
;
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
;
535 CameraAbilitiesList
*al
;
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
563 KConfigGroup
cg(m_config
, model
);
564 cg
.writeEntry("Model", model
);
565 cg
.writeEntry("Path", value
);
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
++) {
576 if (*it
== "<default>")
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)
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");
598 /* Avoid setting usb:xxx,yyy. */
599 if (m_cfgPath
.contains("usb:")>0) {
601 xurl
.setHost("usb:");
603 xurl
.setHost(m_cfgPath
);
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
++) {
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());
625 xurl
.setHost(xurl
.host().replace(":", "___"));
626 entry
.insert(KIO::UDSEntry::UDS_TARGET_URL
,xurl
.url());
628 listEntry(entry
, false);
630 listEntry(entry
, true);
636 if (url
.path().isEmpty()) {
639 kDebug(7123) << "redirecting to /";
640 rooturl
.setPath("/");
641 rooturl
.setHost(url
.host());
642 rooturl
.setUser(url
.user());
643 redirection(rooturl
);
649 error(KIO::ERR_COULD_NOT_READ
,url
.path());
654 CameraList
*fileList
;
655 CameraList
*specialList
;
656 gp_list_new(&dirList
);
657 gp_list_new(&fileList
);
658 gp_list_new(&specialList
);
661 if (!url
.path().compare("/")) {
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
);
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
));
681 totalSize(gp_list_count(specialList
) + gp_list_count(dirList
) + gp_list_count(fileList
));
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);
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("/")) {
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
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
<< ")";
733 if (!theHost
.isEmpty()) {
734 kDebug(7123) << "model is " << user
<< ", port is " << theHost
;
736 kDebug(7123) << "Configuration change detected";
738 gp_camera_unref(m_camera
);
740 infoMessage( i18n("Reinitializing camera") );
742 kDebug(7123) << "Initializing camera";
743 infoMessage( i18n("Initializing camera") );
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
));
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
));
756 gp_abilities_list_get_abilities(abilities_list
, idx
, &m_abilities
);
757 gp_abilities_list_free(abilities_list
);
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:");
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
));
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
);
781 error(KIO::ERR_UNKNOWN
, gp_result_as_string(gpr
));
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
;
797 if (!openCamera(errstr
)) {
799 gp_camera_unref(m_camera
);
801 kDebug(7123) << "Unable to init camera: " << errstr
;
802 error(KIO::ERR_SERVICE_NOT_AVAILABLE
, errstr
);
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
,
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
)
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
));
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
);
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));
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
)
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
<< ")";
897 if((gpr
= gp_camera_folder_list_folders(m_camera
, tocstr(folder
), dirList
, m_context
)) != GP_OK
)
899 if((gpr
= gp_camera_folder_list_files(m_camera
, tocstr(folder
), fileList
, m_context
)) != 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())
920 gp_file_get_data_and_size(object
->getFile(), &fileData
, &fileSize
);
921 // make sure we're not sending zero-sized chunks (=EOF)
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
,
938 KameraProtocol
*object
= (KameraProtocol
*)data
;
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)
947 va_copy(xvalist
, args
);
948 # elif HAVE___VA_COPY
949 __va_copy(xvalist
, args
);
951 int size
=vsnprintf(NULL
, 0, format
, xvalist
);
953 return GP_OK
; // vsnprintf is broken, better don't do anything.
955 status
=new char[size
+1];
957 va_copy(xvalist
, args
);
958 # elif HAVE___VA_COPY
959 __va_copy(xvalist
, args
);
961 vsnprintf(status
, size
+1, format
, xvalist
);
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
);
970 object
->infoMessage(QString::fromLocal8Bit(status
));
972 object
->totalSize((int)totalsize
); // hack: call slot directly
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
;
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)
988 va_copy(xvalist
, args
);
989 # elif HAVE___VA_COPY
990 __va_copy(xvalist
, args
);
992 int size
=vsnprintf(NULL
, 0, format
, xvalist
);
994 return; // vsnprintf is broken, better don't do anything.
996 status
=new char[size
+1];
998 va_copy(xvalist
, args
);
999 # elif HAVE___VA_COPY
1000 __va_copy(xvalist
, args
);
1002 vsnprintf(status
, size
+1, format
, xvalist
);
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
);
1010 object
->infoMessage(QString::fromLocal8Bit(status
));