/* Copyright (C) 2001 The Kompany 2001-2003 Ilya Konstantinov <kde-devel@future.shiny.co.il> 2001-2007 Marcus Meissner <marcus@jet.franken.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <signal.h> #include <errno.h> #include <tqfile.h> #include <tqtextstream.h> #include <kdebug.h> #include <kinstance.h> #include <kstandarddirs.h> #include <tdeconfig.h> #include <ksimpleconfig.h> #include <tdelocale.h> #include <kprotocolinfo.h> #include <tdeio/slaveconfig.h> #include <config.h> #include "kamera.h" #define MAXIDLETIME 30 /* seconds */ #define tocstr(x) ((x).local8Bit()) using namespace TDEIO; extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); #ifdef HAVE_GPHOTO2_5 static void frontendCameraStatus(GPContext *context, const char *status, void *data); static unsigned int frontendProgressStart( GPContext *context, float totalsize, const char *status, void *data ); #else static void frontendCameraStatus(GPContext *context, const char *format, va_list args, void *data); static unsigned int frontendProgressStart( GPContext *context, float totalsize, const char *format, va_list args, void *data ); #endif static void frontendProgressUpdate( GPContext *context, unsigned int id, float current, void *data ); } int kdemain(int argc, char **argv) { TDEInstance instance("tdeio_kamera"); if(argc != 4) { kdDebug(7123) << "Usage: tdeio_kamera protocol " "domain-socket1 domain-socket2" << endl; exit(-1); } KameraProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } KameraProtocol::KameraProtocol(const TQCString &pool, const TQCString &app) : SlaveBase("camera", pool, app), m_camera(NULL) { // attempt to initialize libgphoto2 and chosen camera (requires locking) // (will init m_camera, since the m_camera's configuration is empty) m_camera = 0; m_file = NULL; m_config = new KSimpleConfig(KProtocolInfo::config("camera")); m_context = gp_context_new(); actiondone = true; cameraopen = false; m_modelSpecified = true; m_lockfile = locateLocal("tmp", "kamera"); idletime = 0; } // This handler is getting called every second. We use it to do the // delayed close of the camera. // Logic is: // - No more requests in the queue (signaled by actiondone) AND // - We are MAXIDLETIME seconds idle OR // - Another slave wants to have access to the camera. // // The existance of a lockfile is used to signify "please give up camera". // void KameraProtocol::special(const TQByteArray&) { kdDebug(7123) << "KameraProtocol::special() at " << getpid() << endl; if (!actiondone && cameraopen) { struct stat stbuf; if ((-1!=::stat(m_lockfile.utf8(),&stbuf)) || (idletime++ >= MAXIDLETIME)) { kdDebug(7123) << "KameraProtocol::special() closing camera." << endl; closeCamera(); setTimeoutSpecialCommand(-1); } else { // continue to wait setTimeoutSpecialCommand(1); } } else { // We let it run until the slave gets no actions anymore. setTimeoutSpecialCommand(1); } actiondone = false; } KameraProtocol::~KameraProtocol() { kdDebug(7123) << "KameraProtocol::~KameraProtocol()" << endl; delete m_config; if(m_camera) { closeCamera(); gp_camera_free(m_camera); m_camera = NULL; } } // initializes the camera for usage - should be done before operations over the wire bool KameraProtocol::openCamera(TQString &str) { idletime = 0; actiondone = true; if (!m_camera) { reparseConfiguration(); } else { if (!cameraopen) { int ret, tries = 15; kdDebug(7123) << "KameraProtocol::openCamera at " << getpid() << endl; while (tries--) { ret = gp_camera_init(m_camera, m_context); if ( (ret == GP_ERROR_IO_USB_CLAIM) || (ret == GP_ERROR_IO_LOCK)) { // just create / touch if not there int fd = ::open(m_lockfile.utf8(),O_CREAT|O_WRONLY,0600); if (fd != -1) ::close(fd); ::sleep(1); kdDebug(7123) << "openCamera at " << getpid() << "- busy, ret " << ret << ", trying again." << endl; continue; } if (ret == GP_OK) break; str = gp_result_as_string(ret); return false; } ::unlink(m_lockfile.utf8()); setTimeoutSpecialCommand(1); kdDebug(7123) << "openCamera succeeded at " << getpid() << endl; if (!m_modelSpecified) { gp_camera_get_abilities(m_camera, &m_abilities); m_modelSpecified = true; } cameraopen = true; } } return true; } // should be done after operations over the wire void KameraProtocol::closeCamera(void) { int gpr; if (!m_camera) return; kdDebug(7123) << "KameraProtocol::closeCamera at " << getpid() << endl; if ((gpr=gp_camera_exit(m_camera,m_context))!=GP_OK) { kdDebug(7123) << "closeCamera failed with " << gp_result_as_string(gpr) << endl; } // HACK: gp_camera_exit() in gp 2.0 does not close the port if there // is no camera_exit function. gp_port_close(m_camera->port); cameraopen = false; return; } static TQString fix_foldername(TQString ofolder) { TQString folder = ofolder; if (folder.length() > 1) { while ((folder.length()>1) && (folder.right(1) == "/")) folder = folder.left(folder.length()-1); } if (folder.length() == 0) folder = "/"; return folder; } // The TDEIO slave "get" function (starts a download from the camera) // The actual returning of the data is done in the frontend callback functions. void KameraProtocol::get(const KURL &url) { kdDebug(7123) << "KameraProtocol::get(" << url.path() << ")" << endl; CameraFileType fileType; int gpr; if (url.host().isEmpty()) { error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); return; } if(!openCamera()) { error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); return; } // fprintf(stderr,"get(%s)\n",url.path().latin1()); #define GPHOTO_TEXT_FILE(xx) \ if (!url.path().compare("/" #xx ".txt")) { \ CameraText xx; \ gpr = gp_camera_get_##xx(m_camera, &xx, m_context); \ if (gpr != GP_OK) { \ error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); \ return; \ } \ TQByteArray chunkDataBuffer; \ chunkDataBuffer.setRawData(xx.text, strlen(xx.text)); \ data(chunkDataBuffer); \ processedSize(strlen(xx.text)); \ chunkDataBuffer.resetRawData(xx.text, strlen(xx.text)); \ finished(); \ return; \ } GPHOTO_TEXT_FILE(about); GPHOTO_TEXT_FILE(manual); GPHOTO_TEXT_FILE(summary); #undef GPHOTO_TEXT_FILE // emit info message infoMessage( i18n("Retrieving data from camera <b>%1</b>").arg(url.user()) ); // Note: There's no need to re-read directory for each get() anymore gp_file_new(&m_file); // emit the total size (we must do it before sending data to allow preview) CameraFileInfo info; gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context); if (gpr != GP_OK) { // fprintf(stderr,"Folder %s / File %s not found, gpr is %d\n",folder.latin1(), url.fileName().latin1(), gpr); gp_file_unref(m_file); if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); else error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); return; } // at last, a proper API to determine whether a thumbnail was requested. if(cameraSupportsPreview() && metaData("thumbnail") == "1") { kdDebug(7123) << "get() retrieving the thumbnail" << endl; fileType = GP_FILE_TYPE_PREVIEW; if (info.preview.fields & GP_FILE_INFO_SIZE) totalSize(info.preview.size); if (info.preview.fields & GP_FILE_INFO_TYPE) mimeType(info.preview.type); } else { kdDebug(7123) << "get() retrieving the full-scale photo" << endl; fileType = GP_FILE_TYPE_NORMAL; if (info.file.fields & GP_FILE_INFO_SIZE) totalSize(info.file.size); if (info.preview.fields & GP_FILE_INFO_TYPE) mimeType(info.file.type); } // fetch the data m_fileSize = 0; gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context); if ( (gpr == GP_ERROR_NOT_SUPPORTED) && (fileType == GP_FILE_TYPE_PREVIEW) ) { // If we get here, the file info command information // will either not be used, or still valid. fileType = GP_FILE_TYPE_NORMAL; gpr = gp_camera_file_get(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), fileType, m_file, m_context); } switch(gpr) { case GP_OK: break; case GP_ERROR_FILE_NOT_FOUND: case GP_ERROR_DIRECTORY_NOT_FOUND: gp_file_unref(m_file); m_file = NULL; error(TDEIO::ERR_DOES_NOT_EXIST, url.fileName()); return ; default: gp_file_unref(m_file); m_file = NULL; error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); return; } // emit the mimetype // NOTE: we must first get the file, so that CameraFile->name would be set const char *fileMimeType; gp_file_get_mime_type(m_file, &fileMimeType); mimeType(fileMimeType); // We need to pass left over data here. Some camera drivers do not // implement progress callbacks! const char *fileData; long unsigned int fileSize; // This merely returns us a pointer to gphoto's internal data // buffer -- there's no expensive memcpy gpr = gp_file_get_data_and_size(m_file, &fileData, &fileSize); if (gpr != GP_OK) { kdDebug(7123) << "get():: get_data_and_size failed." << endl; gp_file_free(m_file); m_file = NULL; error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); return; } // make sure we're not sending zero-sized chunks (=EOF) // also make sure we send only if the progress did not send the data // already. if ((fileSize > 0) && (fileSize - m_fileSize)>0) { unsigned long written = 0; TQByteArray chunkDataBuffer; // We need to split it up here. Someone considered it funny // to discard any data() larger than 16MB. // // So nearly any Movie will just fail.... while (written < fileSize-m_fileSize) { unsigned long towrite = 1024*1024; // 1MB if (towrite > fileSize-m_fileSize-written) towrite = fileSize-m_fileSize-written; chunkDataBuffer.setRawData(fileData + m_fileSize + written, towrite); processedSize(m_fileSize + written + towrite); data(chunkDataBuffer); chunkDataBuffer.resetRawData(fileData + m_fileSize + written, towrite); written += towrite; } m_fileSize = fileSize; setFileSize(fileSize); } finished(); gp_file_unref(m_file); /* just unref, might be stored in fs */ m_file = NULL; } // The TDEIO slave "stat" function. void KameraProtocol::stat(const KURL &url) { kdDebug(7123) << "stat(\"" << url.path() << "\")" << endl; if (url.path() == "") { KURL rooturl(url); kdDebug(7123) << "redirecting to /" << endl; rooturl.setPath("/"); rooturl.setHost(url.host()); rooturl.setUser(url.user()); redirection(rooturl); finished(); return; } if(url.path() == "/") statRoot(); else statRegular(url); } // Implements stat("/") -- which always returns the same value. void KameraProtocol::statRoot(void) { UDSEntry entry; UDSAtom atom; atom.m_uds = UDS_NAME; atom.m_str = "/"; entry.append(atom); atom.m_uds = UDS_FILE_TYPE; atom.m_long = S_IFDIR; entry.append(atom); atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; entry.append(atom); statEntry(entry); finished(); // This call happens on autodetect by kdemm. So close the camera, but // only if no more requests are pending. idletime = MAXIDLETIME; } // Implements a regular stat() of a file / directory, returning all we know about it void KameraProtocol::statRegular(const KURL &url) { UDSEntry entry; int gpr; kdDebug(7123) << "statRegular(\"" << url.path() << "\")" << endl; if (openCamera() == false) { error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); return; } // fprintf(stderr,"statRegular(%s)\n",url.path().latin1()); // Is "url" a directory? CameraList *dirList; gp_list_new(&dirList); kdDebug(7123) << "statRegular() Requesting directories list for " << url.directory() << endl; gpr = gp_camera_folder_list_folders(m_camera, tocstr(fix_foldername(url.directory(false))), dirList, m_context); if (gpr != GP_OK) { if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); else error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); gp_list_free(dirList); return; } #define GPHOTO_TEXT_FILE(xx) \ if (!url.path().compare("/"#xx".txt")) { \ CameraText xx; \ gpr = gp_camera_get_about(m_camera, &xx, m_context); \ if (gpr != GP_OK) { \ error(TDEIO::ERR_DOES_NOT_EXIST, url.fileName()); \ return; \ } \ translateTextToUDS(entry,#xx".txt",xx.text); \ statEntry(entry); \ finished(); \ return; \ } GPHOTO_TEXT_FILE(about); GPHOTO_TEXT_FILE(manual); GPHOTO_TEXT_FILE(summary); #undef GPHOTO_TEXT_FILE const char *name; for(int i = 0; i < gp_list_count(dirList); i++) { gp_list_get_name(dirList, i, &name); if (url.fileName().compare(name) == 0) { gp_list_free(dirList); UDSEntry entry; translateDirectoryToUDS(entry, url.fileName()); statEntry(entry); finished(); return; } } gp_list_free(dirList); // Is "url" a file? CameraFileInfo info; gpr = gp_camera_file_get_info(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), &info, m_context); if (gpr != GP_OK) { if ((gpr == GP_ERROR_FILE_NOT_FOUND) || (gpr == GP_ERROR_DIRECTORY_NOT_FOUND)) error(TDEIO::ERR_DOES_NOT_EXIST, url.path()); else error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); return; } translateFileToUDS(entry, info, url.fileName()); statEntry(entry); finished(); } // The TDEIO slave "del" function. void KameraProtocol::del(const KURL &url, bool isFile) { kdDebug(7123) << "KameraProtocol::del(" << url.path() << ")" << endl; if(!openCamera()) { error(TDEIO::ERR_CANNOT_DELETE, url.fileName()); return; } if (!cameraSupportsDel()) { error(TDEIO::ERR_CANNOT_DELETE, url.fileName()); return; } if(isFile){ CameraList *list; gp_list_new(&list); int ret; ret = gp_camera_file_delete(m_camera, tocstr(fix_foldername(url.directory(false))), tocstr(url.fileName()), m_context); if(ret != GP_OK) { error(TDEIO::ERR_CANNOT_DELETE, url.fileName()); } else { finished(); } } } // The TDEIO slave "listDir" function. void KameraProtocol::listDir(const KURL &url) { kdDebug(7123) << "KameraProtocol::listDir(" << url.path() << ")" << endl; if (url.host().isEmpty()) { KURL xurl; // List the available cameras TQStringList groupList = m_config->groupList(); kdDebug(7123) << "Found cameras: " << groupList.join(", ") << endl; TQStringList::Iterator it; UDSEntry entry; UDSAtom atom; /* * What we do: * - Autodetect cameras and remember them with their ports. * - List all saved and possible offline cameras. * - List all autodetected and not yet printed cameras. */ TQMap<TQString,TQString> ports, names; TQMap<TQString,int> modelcnt; /* Autodetect USB cameras ... */ GPContext *glob_context = NULL; int i, count; CameraList *list; CameraAbilitiesList *al; GPPortInfoList *il; gp_list_new (&list); gp_abilities_list_new (&al); gp_abilities_list_load (al, glob_context); gp_port_info_list_new (&il); gp_port_info_list_load (il); gp_abilities_list_detect (al, il, list, glob_context); gp_abilities_list_free (al); gp_port_info_list_free (il); count = gp_list_count (list); for (i = 0 ; i<count ; i++) { const char *model, *value; gp_list_get_name (list, i, &model); gp_list_get_value (list, i, &value); ports[value] = model; // NOTE: We might get different ports than usb: later! if (strcmp(value, "usb:")) names[model] = value; /* Save them, even though we can autodetect them for * offline listing. */ m_config->setGroup(model); m_config->writeEntry("Model",model); m_config->writeEntry("Path",value); modelcnt[model]++; } gp_list_free (list); /* Avoid duplicated entry for usb: and usb:001,042 entries. */ if (ports.contains("usb:") && names[ports["usb:"]]!="usb:") ports.remove("usb:"); for (it = groupList.begin(); it != groupList.end(); it++) { TQString m_cfgPath; if (*it == "<default>") continue; m_config->setGroup(*it); m_cfgPath = m_config->readEntry("Path"); /* If autodetect by USB autodetect ... skip it here. * We leave unattached USB cameras in here, because the user * might plug them in later and does not want to press reload. * We add them with port "usb:". */ if (modelcnt[*it] > 0) continue; entry.clear(); atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR;entry.append(atom); atom.m_uds = UDS_NAME;atom.m_str = *it;entry.append(atom); atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; entry.append(atom); atom.m_uds = UDS_URL; xurl.setProtocol("camera"); xurl.setUser(*it); /* Avoid setting usb:xxx,yyy. */ if (m_cfgPath.contains("usb:")>0) { names[*it] = "usb:"; xurl.setHost("usb:"); } else { xurl.setHost(m_cfgPath); } xurl.setPath("/"); atom.m_str = xurl.url(); entry.append(atom); listEntry(entry, false); } TQMap<TQString,TQString>::iterator portsit; for (portsit = ports.begin(); portsit != ports.end(); portsit++) { entry.clear(); atom.m_uds = UDS_FILE_TYPE;atom.m_long = S_IFDIR; entry.append(atom); atom.m_uds = UDS_NAME;atom.m_str = portsit.data();entry.append(atom); atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; entry.append(atom); atom.m_uds = UDS_URL; xurl.setProtocol("camera"); xurl.setHost(portsit.key()); xurl.setUser(portsit.data()); xurl.setPath("/"); atom.m_str = xurl.url(); entry.append(atom); listEntry(entry, false); } listEntry(entry, true); finished(); return; } if (url.path() == "") { KURL rooturl(url); kdDebug(7123) << "redirecting to /" << endl; rooturl.setPath("/"); rooturl.setHost(url.host()); rooturl.setUser(url.user()); redirection(rooturl); finished(); return; } if (!openCamera()) { error(TDEIO::ERR_COULD_NOT_READ,url.path()); return; } CameraList *dirList; CameraList *fileList; CameraList *specialList; gp_list_new(&dirList); gp_list_new(&fileList); gp_list_new(&specialList); int gpr; if (!url.path().compare("/")) { CameraText text; if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) gp_list_append(specialList,"manual.txt",NULL); if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) gp_list_append(specialList,"about.txt",NULL); if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) gp_list_append(specialList,"summary.txt",NULL); } gpr = readCameraFolder(url.path(), dirList, fileList); if(gpr != GP_OK) { kdDebug(7123) << "read Camera Folder failed:" << gp_result_as_string(gpr) <<endl; gp_list_free(dirList); gp_list_free(fileList); gp_list_free(specialList); error(TDEIO::ERR_COULD_NOT_READ, gp_result_as_string(gpr)); return; } totalSize(gp_list_count(specialList) + gp_list_count(dirList) + gp_list_count(fileList)); UDSEntry entry; const char *name; for(int i = 0; i < gp_list_count(dirList); ++i) { gp_list_get_name(dirList, i, &name); translateDirectoryToUDS(entry, TQString::fromLocal8Bit(name)); listEntry(entry, false); } CameraFileInfo info; for(int i = 0; i < gp_list_count(fileList); ++i) { gp_list_get_name(fileList, i, &name); // we want to know more info about files (size, type...) gp_camera_file_get_info(m_camera, tocstr(url.path()), name, &info, m_context); translateFileToUDS(entry, info, TQString::fromLocal8Bit(name)); listEntry(entry, false); } if (!url.path().compare("/")) { CameraText text; if (GP_OK == gp_camera_get_manual(m_camera, &text, m_context)) { translateTextToUDS(entry, "manual.txt", text.text); listEntry(entry, false); } if (GP_OK == gp_camera_get_about(m_camera, &text, m_context)) { translateTextToUDS(entry, "about.txt", text.text); listEntry(entry, false); } if (GP_OK == gp_camera_get_summary(m_camera, &text, m_context)) { translateTextToUDS(entry, "summary.txt", text.text); listEntry(entry, false); } } gp_list_free(fileList); gp_list_free(dirList); gp_list_free(specialList); listEntry(entry, true); // 'entry' is not used in this case - we only signal list completion finished(); } void KameraProtocol::setHost(const TQString& host, int port, const TQString& user, const TQString& pass ) { kdDebug(7123) << "KameraProtocol::setHost(" << host << ", " << port << ", " << user << ", " << pass << ")" << endl; int gpr, idx; if (!host.isEmpty()) { kdDebug(7123) << "model is " << user << ", port is " << host << endl; if (m_camera) { kdDebug(7123) << "Configuration change detected" << endl; closeCamera(); gp_camera_unref(m_camera); m_camera = NULL; infoMessage( i18n("Reinitializing camera") ); } else { kdDebug(7123) << "Initializing camera" << endl; infoMessage( i18n("Initializing camera") ); } // fetch abilities CameraAbilitiesList *abilities_list; gp_abilities_list_new(&abilities_list); gp_abilities_list_load(abilities_list, m_context); idx = gp_abilities_list_lookup_model(abilities_list, tocstr(user)); if (idx < 0) { gp_abilities_list_free(abilities_list); kdDebug(7123) << "Unable to get abilities for model: " << user << ", falling back to automatic model detection" << endl; m_modelSpecified = false; } if (m_modelSpecified) { gp_abilities_list_get_abilities(abilities_list, idx, &m_abilities); gp_abilities_list_free(abilities_list); } // fetch port GPPortInfoList *port_info_list; GPPortInfo port_info; gp_port_info_list_new(&port_info_list); gp_port_info_list_load(port_info_list); idx = gp_port_info_list_lookup_path(port_info_list, tocstr(host)); /* Handle erronously passed usb:XXX,YYY */ if ((idx < 0) && host.startsWith("usb:")) idx = gp_port_info_list_lookup_path(port_info_list, "usb:"); if (idx < 0) { gp_port_info_list_free(port_info_list); kdDebug(7123) << "Unable to get port info for path: " << host << endl; error(TDEIO::ERR_UNKNOWN, gp_result_as_string(idx)); return; } gp_port_info_list_get_info(port_info_list, idx, &port_info); // create a new camera object gpr = gp_camera_new(&m_camera); if(gpr != GP_OK) { gp_port_info_list_free(port_info_list); error(TDEIO::ERR_UNKNOWN, gp_result_as_string(gpr)); return; } // register gphoto2 callback functions gp_context_set_status_func(m_context, frontendCameraStatus, this); gp_context_set_progress_funcs(m_context, frontendProgressStart, frontendProgressUpdate, NULL, this); // gp_camera_set_message_func(m_camera, ..., this) // set model and port if (m_modelSpecified) { gp_camera_set_abilities(m_camera, m_abilities); } gp_camera_set_port_info(m_camera, port_info); gp_camera_set_port_speed(m_camera, 0); // TODO: the value needs to be configurable kdDebug(7123) << "Opening camera model " << user << " at " << host << endl; gp_port_info_list_free(port_info_list); TQString errstr; if (!openCamera(errstr)) { kdDebug(7123) << "Unable to init camera: " << gp_result_as_string(gpr) << endl; error(TDEIO::ERR_SERVICE_NOT_AVAILABLE, errstr); gp_camera_exit(m_camera, m_context); return; } } } void KameraProtocol::reparseConfiguration(void) { // we have no global config, do we? } // translate a simple text to a UDS entry void KameraProtocol::translateTextToUDS(UDSEntry &udsEntry, const TQString &fn, const char *text ) { UDSAtom atom; udsEntry.clear(); atom.m_uds = UDS_FILE_TYPE; // UDS type atom.m_long = S_IFREG; // file udsEntry.append(atom); atom.m_uds = UDS_NAME; atom.m_str = fn; udsEntry.append(atom); atom.m_uds = UDS_SIZE; atom.m_long = strlen(text); udsEntry.append(atom); atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; udsEntry.append(atom); } // translate a CameraFileInfo to a UDSEntry which we can return as a directory listing entry void KameraProtocol::translateFileToUDS(UDSEntry &udsEntry, const CameraFileInfo &info, TQString name) { UDSAtom atom; udsEntry.clear(); atom.m_uds = UDS_FILE_TYPE; // UDS type atom.m_long = S_IFREG; // file udsEntry.append(atom); atom.m_uds = UDS_NAME; atom.m_str = name; udsEntry.append(atom); if (info.file.fields & GP_FILE_INFO_SIZE) { atom.m_uds = UDS_SIZE; atom.m_long = info.file.size; udsEntry.append(atom); } if (info.file.fields & GP_FILE_INFO_MTIME) { atom.m_uds = UDS_MODIFICATION_TIME; atom.m_long = info.file.mtime; udsEntry.append(atom); } else { atom.m_uds = UDS_MODIFICATION_TIME; atom.m_long = time(NULL); /* NOW */ udsEntry.append(atom); } if (info.file.fields & GP_FILE_INFO_TYPE) { atom.m_uds = UDS_MIME_TYPE; atom.m_str = TQString::fromLatin1(info.file.type); udsEntry.append(atom); } if (info.file.fields & GP_FILE_INFO_PERMISSIONS) { atom.m_uds = UDS_ACCESS; atom.m_long = 0; atom.m_long |= (info.file.permissions & GP_FILE_PERM_READ) ? (S_IRUSR | S_IRGRP | S_IROTH) : 0; // we cannot represent individual FP_FILE_PERM_DELETE permission in the Unix access scheme // since the parent directory's write permission defines that udsEntry.append(atom); } else { // basic permissions, in case the camera doesn't provide permissions info atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; udsEntry.append(atom); } // TODO: We do not handle info.preview in any way } // translate a directory name to a UDSEntry which we can return as a directory listing entry void KameraProtocol::translateDirectoryToUDS(UDSEntry &udsEntry, const TQString &dirname) { UDSAtom atom; udsEntry.clear(); atom.m_uds = UDS_FILE_TYPE; // UDS type atom.m_long = S_IFDIR; // directory udsEntry.append(atom); atom.m_uds = UDS_NAME; atom.m_str = dirname; udsEntry.append(atom); atom.m_uds = UDS_ACCESS; atom.m_long = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; udsEntry.append(atom); atom.m_uds = UDS_MIME_TYPE; atom.m_str = "inode/directory"; udsEntry.append(atom); } bool KameraProtocol::cameraSupportsDel(void) { return (m_abilities.file_operations & GP_FILE_OPERATION_DELETE); } bool KameraProtocol::cameraSupportsPut(void) { return (m_abilities.folder_operations & GP_FOLDER_OPERATION_PUT_FILE); } bool KameraProtocol::cameraSupportsPreview(void) { return (m_abilities.file_operations & GP_FILE_OPERATION_PREVIEW); } int KameraProtocol::readCameraFolder(const TQString &folder, CameraList *dirList, CameraList *fileList) { kdDebug(7123) << "KameraProtocol::readCameraFolder(" << folder << ")" << endl; int gpr; if((gpr = gp_camera_folder_list_folders(m_camera, tocstr(folder), dirList, m_context)) != GP_OK) return gpr; if((gpr = gp_camera_folder_list_files(m_camera, tocstr(folder), fileList, m_context)) != GP_OK) return gpr; return GP_OK; } void frontendProgressUpdate( GPContext * /*context*/, unsigned int /*id*/, float /*current*/, void *data ) { KameraProtocol *object = (KameraProtocol*)data; // This code will get the last chunk of data retrieved from the // camera and pass it to TDEIO, to allow progressive display // of the downloaded photo. const char *fileData = NULL; long unsigned int fileSize = 0; // This merely returns us a pointer to gphoto's internal data // buffer -- there's no expensive memcpy if (!object->getFile()) return; gp_file_get_data_and_size(object->getFile(), &fileData, &fileSize); // make sure we're not sending zero-sized chunks (=EOF) if (fileSize > 0) { // XXX using assign() here causes segfault, prolly because // gp_file_free is called before chunkData goes out of scope TQByteArray chunkDataBuffer; chunkDataBuffer.setRawData(fileData + object->getFileSize(), fileSize - object->getFileSize()); // Note: this will fail with sizes > 16MB ... object->data(chunkDataBuffer); object->processedSize(fileSize); chunkDataBuffer.resetRawData(fileData + object->getFileSize(), fileSize - object->getFileSize()); object->setFileSize(fileSize); } } unsigned int frontendProgressStart( GPContext * /*context*/, float totalsize, #ifdef HAVE_GPHOTO2_5 const char *status, #else const char *format, va_list args, #endif void *data ) { KameraProtocol *object = (KameraProtocol*)data; #ifndef HAVE_GPHOTO2_5 char *status; /* We must copy the va_list to walk it twice, or all hell * breaks loose on non-i386 platforms. */ #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY) va_list xvalist; # ifdef HAVE_VA_COPY va_copy(xvalist, args); # elif HAVE___VA_COPY __va_copy(xvalist, args); # endif int size=vsnprintf(NULL, 0, format, xvalist); if(size<=0) return GP_OK; // vsnprintf is broken, better don't do anything. status=new char[size+1]; # ifdef HAVE_VA_COPY va_copy(xvalist, args); # elif HAVE___VA_COPY __va_copy(xvalist, args); # endif vsnprintf(status, size+1, format, xvalist); #else /* We cannot copy the va_list, so make sure we * walk it just _once_. */ status=new char[300]; vsnprintf(status, 300, format, args); #endif object->infoMessage(TQString::fromLocal8Bit(status)); delete [] status; #else /* libgphoto2 2.5 has resolved this already, no need for print */ object->infoMessage(TQString::fromLocal8Bit(status)); #endif object->totalSize((int)totalsize); // hack: call slot directly return GP_OK; } // this callback function is activated on every status message from gphoto2 static void frontendCameraStatus( GPContext * /*context*/, #ifdef HAVE_GPHOTO2_5 const char *status, #else const char *format, va_list args, #endif void *data ) { KameraProtocol *object = (KameraProtocol*)data; #ifndef HAVE_GPHOTO2_5 char *status; /* We must copy the va_list to walk it twice, or all hell * breaks loose on non-i386 platforms. */ #if defined(HAVE_VA_COPY) || defined(HAVE___VA_COPY) va_list xvalist; # ifdef HAVE_VA_COPY va_copy(xvalist, args); # elif HAVE___VA_COPY __va_copy(xvalist, args); # endif int size=vsnprintf(NULL, 0, format, xvalist); if(size<=0) return; // vsnprintf is broken, better don't do anything. status=new char[size+1]; # ifdef HAVE_VA_COPY va_copy(xvalist, args); # elif HAVE___VA_COPY __va_copy(xvalist, args); # endif vsnprintf(status, size+1, format, xvalist); #else /* We cannot copy the va_list, so make sure we * walk it just _once_. */ status=new char[300]; vsnprintf(status, 300, format, args); #endif object->infoMessage(TQString::fromLocal8Bit(status)); delete [] status; #else object->infoMessage(TQString::fromLocal8Bit(status)); #endif }