Source code upload
[framework/connectivity/libgphoto2.git] / camlibs / ricoh / g3.c
1 /* g3.c
2  *
3  * Copyright © 2003 Marcus Meissner <marcus@jet.franken.de>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, 
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details. 
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #include "config.h"
21
22 #include <stdio.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include <gphoto2/gphoto2-library.h>
29 #include <gphoto2/gphoto2-result.h>
30 #include <gphoto2/gphoto2-port-log.h>
31
32 #ifdef ENABLE_NLS
33 #  include <libintl.h>
34 #  undef _
35 #  define _(String) dgettext (GETTEXT_PACKAGE, String)
36 #  ifdef gettext_noop
37 #    define N_(String) gettext_noop (String)
38 #  else
39 #    define N_(String) (String)
40 #  endif
41 #else
42 #  define _(String) (String)
43 #  define N_(String) (String)
44 #endif
45
46 /* channel header: 
47  *
48  * channel  host/client  length       data     checksum padding
49  * 01 01    00 00        07 00 00 00  <7 byte> <1 byte> <fill to next 4>
50  * 
51  */
52
53 static int
54 g3_channel_read(GPPort *port, int *channel, char **buffer, int *len)
55 {
56         unsigned char xbuf[0x800];
57         int tocopy, ret, curlen;
58
59         ret = gp_port_read(port, xbuf, 0x800);
60         if (ret < GP_OK) { 
61                 gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
62                 return ret;
63         }
64
65         if ((xbuf[2] != 0xff) && (xbuf[3] != 0xff)) {
66                 gp_log(GP_LOG_ERROR, "g3" ,"first bytes do not match.\n");
67                 return GP_ERROR_IO;
68         }
69
70         *channel = xbuf[1];
71         *len = xbuf[4] + (xbuf[5]<<8) + (xbuf[6]<<16) + (xbuf[7]<<24);
72         /* Safety buffer of 0x800 ... we can only read in 0x800 chunks, 
73          * otherwise the communication gets hickups. However *len might be
74          * less.
75          */
76         if (!*buffer)
77                 *buffer = malloc(*len + 1 + 0x800);
78         else
79                 *buffer = realloc(*buffer, *len + 1 + 0x800);
80         tocopy = *len;
81         if (tocopy > 0x800-8) tocopy = 0x800-8;
82         memcpy(*buffer, xbuf+8, tocopy);
83         curlen = tocopy;
84         while (curlen < *len) {
85                 ret = gp_port_read(port, *buffer + curlen, 0x800);
86                 if (ret < GP_OK) {
87                         gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
88                         return ret;
89                 }
90                 curlen += ret;
91         }
92         (*buffer)[*len] = 0x00;
93         return GP_OK;
94 }
95
96 static int
97 g3_channel_read_bytes(
98         GPPort *port, int *channel, char **buffer, int expected, GPContext *context,
99         const char *msg
100 ) {
101         unsigned char *xbuf;
102         int id, ret, len, xlen = 0;
103
104         if (!*buffer)
105                 *buffer = malloc (expected);
106         else
107                 *buffer = realloc (*buffer, expected);
108         xbuf = malloc(65536 + 12);
109
110         id = gp_context_progress_start (context, expected, "%s", msg);
111
112         /* The camera lets us read in packets of at least max 64kb size. */
113         while (expected > 0) {
114                 int rest = expected;
115                 if (rest > 65536) rest = 65536;
116
117                 rest = (rest + 9 + 3) & ~3;
118                 if (rest < 0x800) rest = 0x800;
119
120                 ret = gp_port_read(port, xbuf, rest);
121                 if (ret < GP_OK) {
122                         gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
123                         return ret;
124                 }
125                 if (ret != rest) {
126                         gp_log(GP_LOG_ERROR, "g3", "read error in g3_channel_read\n");
127                         return ret;
128                 }
129
130                 if ((xbuf[2] != 0xff) || (xbuf[3] != 0xff)) {
131                         gp_log(GP_LOG_ERROR, "g3", "first bytes do not match.\n");
132                         free(xbuf);
133                         return GP_ERROR_IO;
134                 }
135                 len = xbuf[4] + (xbuf[5]<<8) + (xbuf[6]<<16) + (xbuf[7]<<24);
136                 *channel = xbuf[1];
137                 if (len > expected)
138                         gp_log(GP_LOG_ERROR,"g3","len %d is > rest expected %d\n", len, expected);
139                 memcpy(*buffer+xlen, xbuf+8, len);
140                 expected        -= len;
141                 xlen            += len;
142                 gp_context_progress_update (context, id, xlen);
143         }
144         gp_context_progress_stop (context, id);
145         free(xbuf);
146         return GP_OK;
147 }
148
149 static int
150 g3_channel_write(GPPort *port, int channel, char *buffer, int len)
151 {
152         unsigned char *xbuf;
153         int ret = GP_OK, nlen, curlen = 0;
154
155         while (len > 0) {
156                 int sendlen = len;
157                 
158                 if (sendlen>65536) sendlen = 65536;
159                 nlen = (4 + 4 + sendlen + 1 +  3) & ~3;
160                 xbuf = calloc(nlen,1);
161
162                 xbuf[0] = 1;
163                 xbuf[1] = channel;
164                 xbuf[2] = 0;
165                 xbuf[3] = 0;
166                 xbuf[4] =  sendlen      & 0xff;
167                 xbuf[5] = (sendlen>>8)  & 0xff;
168                 xbuf[6] = (sendlen>>16) & 0xff;
169                 xbuf[7] = (sendlen>>24) & 0xff;
170                 memcpy(xbuf+8, buffer + curlen, sendlen);
171                 curlen += sendlen;
172                 xbuf[8+sendlen] = 0x03;
173                 ret = gp_port_write( port, (char*)xbuf, nlen);
174                 free(xbuf);
175                 if (ret < GP_OK) break;
176                 len -= sendlen;
177         }
178         return ret;
179 }
180
181 static int
182 g3_ftp_command_and_reply(GPPort *port, char *cmd, char **reply) {
183         int ret, channel, len;
184         char *realcmd, *s;
185
186         realcmd = malloc(strlen(cmd)+2+1);strcpy(realcmd, cmd);strcat(realcmd, "\r\n");
187
188         gp_log(GP_LOG_DEBUG, "g3" , "sending %s", cmd);
189         ret = g3_channel_write(port, 1, realcmd, strlen(realcmd));
190         free(realcmd);
191         if (ret < GP_OK) {
192                 gp_log (GP_LOG_ERROR, "g3", "ftp command write failed? %d\n", ret);
193                 return ret;
194         }
195         ret = g3_channel_read(port, &channel, reply, &len);
196         if (ret < GP_OK) {
197                 gp_log (GP_LOG_ERROR, "g3", "ftp reply read failed? %d\n", ret);
198                 return ret;
199         }
200         s = strchr(*reply, '\r');
201         if (s) *s='\0';
202
203         gp_log(GP_LOG_DEBUG, "g3" , "reply %s", *reply);
204         return GP_OK;
205 }
206
207 int
208 camera_id (CameraText *id) 
209 {
210         strcpy(id->text, "ricoh_g3");
211         return (GP_OK);
212 }
213
214 int
215 camera_abilities (CameraAbilitiesList *list) 
216 {
217         CameraAbilities a;
218
219         memset(&a, 0, sizeof(a));
220         strcpy(a.model, "Ricoh:Caplio G3");
221         a.status        = GP_DRIVER_STATUS_PRODUCTION;
222         a.port          = GP_PORT_USB;
223         a.usb_vendor    = 0x5ca;
224         a.usb_product   = 0x2204;
225
226         a.operations        =   GP_OPERATION_NONE;
227         a.file_operations   =   GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_EXIF;
228         a.folder_operations =   GP_FOLDER_OPERATION_MAKE_DIR |
229                                 GP_FOLDER_OPERATION_REMOVE_DIR ;
230
231         gp_abilities_list_append(list, a);
232
233         strcpy(a.model, "Ricoh:Caplio RR30");
234         a.usb_vendor    = 0x5ca;
235         a.usb_product   = 0x2202;
236         gp_abilities_list_append(list, a);
237
238         strcpy(a.model, "Ricoh:Caplio 300G");
239         a.usb_vendor    = 0x5ca;
240         a.usb_product   = 0x2203;
241         gp_abilities_list_append(list, a);
242
243         strcpy(a.model, "Medion:MD 6126");
244         a.usb_vendor    = 0x5ca;
245         a.usb_product   = 0x2205;
246         gp_abilities_list_append(list, a);
247
248         strcpy(a.model, "Ricoh:Caplio G4");
249         a.usb_vendor    = 0x5ca;
250         a.usb_product   = 0x2208;
251         gp_abilities_list_append(list, a);
252
253         strcpy(a.model, "Ricoh:Capilo RX");
254         a.usb_vendor    = 0x5ca;
255         a.usb_product   = 0x220b;
256         gp_abilities_list_append(list, a);
257
258         strcpy(a.model, "Ricoh:Caplio GX");
259         a.usb_vendor    = 0x5ca;
260         a.usb_product   = 0x220c;
261         gp_abilities_list_append(list, a);
262
263         strcpy(a.model, "Ricoh:Caplio R1");
264         a.usb_vendor    = 0x5ca;
265         a.usb_product   = 0x220d;
266         gp_abilities_list_append(list, a);
267
268         /* same id as above */
269         strcpy(a.model, "Ricoh:Caplio RZ1");
270         a.usb_vendor    = 0x5ca;
271         a.usb_product   = 0x220d;
272         gp_abilities_list_append(list, a);
273
274         strcpy(a.model, "Sea & Sea:5000G");
275         a.usb_vendor    = 0x5ca;
276         a.usb_product   = 0x220e;
277         gp_abilities_list_append(list, a);
278
279         strcpy(a.model, "Rollei:dr5");
280         a.usb_vendor    = 0x5ca;
281         a.usb_product   = 0x220f;
282         gp_abilities_list_append(list, a);
283
284         strcpy(a.model, "Ricoh:Caplio R1v");
285         a.usb_vendor    = 0x5ca;
286         a.usb_product   = 0x2212;
287         gp_abilities_list_append(list, a);
288
289         strcpy(a.model, "Ricoh:Caplio R2");
290         a.usb_vendor    = 0x5ca;
291         a.usb_product   = 0x2213;
292         gp_abilities_list_append(list, a);
293
294         strcpy(a.model, "Ricoh:Caplio GX 8");
295         a.usb_vendor    = 0x5ca;
296         a.usb_product   = 0x2214;
297         gp_abilities_list_append(list, a);
298
299         strcpy(a.model, "Ricoh:Caplio R3");
300         a.usb_vendor    = 0x5ca;
301         a.usb_product   = 0x2216;
302         gp_abilities_list_append(list, a);
303
304         strcpy(a.model, "Ricoh:Caplio R4");
305         a.usb_vendor    = 0x5ca;
306         a.usb_product   = 0x2217;
307         gp_abilities_list_append(list, a);
308
309         strcpy(a.model, "Ricoh:Caplio R5");
310         a.usb_vendor    = 0x5ca;
311         a.usb_product   = 0x221a;
312         gp_abilities_list_append(list, a);
313
314         return (GP_OK);
315 }
316
317 static int
318 g3_cwd_command( GPPort *port, const char *folder) {
319         char *cmd, *reply = NULL;
320         int ret;
321
322         cmd = malloc(strlen("CWD ") + strlen(folder) + 2 + 1);
323         sprintf(cmd,"CWD %s", folder);
324         ret = g3_ftp_command_and_reply(port, cmd, &reply);
325         free(cmd);
326         if (ret < GP_OK) {
327                 if (reply) free(reply);
328                 return ret;
329         }
330         if (reply[0]=='5') /* Failed, most likely no such directory */
331                 ret = GP_ERROR_DIRECTORY_NOT_FOUND;
332         free(reply);
333         return ret;
334 }
335
336 static int
337 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
338                CameraFileType type, CameraFile *file, void *data,
339                GPContext *context)
340 {
341         Camera *camera = data;
342         char *buf = NULL, *reply = NULL, *cmd =NULL, *msg = NULL;
343         int ret, channel, bytes, seek, len;
344
345         ret = g3_cwd_command (camera->port, folder);
346         if (ret < GP_OK) goto out;
347
348         switch (type) {
349         case GP_FILE_TYPE_NORMAL:
350                 msg = _("Downloading...");
351                 if (strstr(filename,"AVI") || strstr(filename,"avi"))
352                         msg = _("Downloading movie...");
353                 if (strstr(filename,"jpg") || strstr(filename,"JPG") ||
354                     strstr(filename,"tif") || strstr(filename,"TIF")
355                 )
356                         msg = _("Downloading image...");
357                 if (strstr(filename,"wav") || strstr(filename,"WAV"))
358                         msg = _("Downloading audio...");
359                 cmd = malloc(strlen("RETR ") + strlen(filename) + 2 + 1);
360                 sprintf(cmd,"RETR %s", filename);
361                 ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
362                 free(cmd);
363                 if (ret < GP_OK) goto out;
364                 if (buf[0] != '1') { /* error, most likely file not found */
365                         ret = GP_ERROR_FILE_NOT_FOUND;
366                         goto out;
367                 }
368
369                 bytes = 0;
370                 sscanf(buf, "150 data connection for RETR.(%d)", &bytes);
371                 break;
372         case GP_FILE_TYPE_EXIF:
373                 msg = _("Downloading EXIF data...");
374                 if (!strstr(filename,".JPG") && !strstr(filename,".jpg")) {
375                         gp_context_error (context,_("No EXIF data available for %s."),
376                                           filename);
377                         ret = GP_ERROR_FILE_NOT_FOUND;
378                         goto out;
379                 }
380                 cmd = malloc(strlen("-SRET ") + strlen(filename) + 2 + 1);
381                 sprintf(cmd,"-SRET %s", filename);
382                 ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
383                 free(cmd);
384                 if (ret < GP_OK) goto out;
385                 if (buf[0] != '1') { /* error, most likely file not found */
386                         ret = GP_ERROR_FILE_NOT_FOUND;
387                         goto out;
388                 }
389                 bytes = seek = 0;
390                 sscanf(buf, "150 %d byte Seek=%d", &bytes, &seek);
391                 if (seek == -2) {
392                         /* FIXME: pretty bad, the camera has some time out problems
393                          * if this happens */
394                         gp_context_error (context,_("No EXIF data available for %s."),
395                                           filename);
396                         ret = GP_ERROR_FILE_NOT_FOUND;
397                         g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
398                         goto out;
399                 }
400                 bytes += seek;
401                 break;
402         default:
403                 return GP_ERROR_NOT_SUPPORTED;
404         }
405
406         ret = g3_channel_read_bytes(camera->port, &channel, &buf, bytes, context, msg);
407         if (ret < GP_OK) goto out;
408         ret = g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
409         if (ret < GP_OK) goto out;
410         gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
411         gp_file_set_data_and_size (file, buf, bytes);
412         buf = NULL; /* now owned by libgphoto2 filesystem */
413
414 out:
415         if (buf) free(buf);
416         if (reply) free(reply);
417         return (GP_OK);
418 }
419
420 #if 0
421 /* Works to some degree, but the firmware on the camera is not really happy
422  * with it and sometimes refuses to send data the correct way
423  */
424 static int
425 put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file,
426                void *data, GPContext *context)
427 {
428         Camera *camera = data;
429         char *buf = NULL, *reply = NULL, *cmd =NULL;
430         const char *fn = NULL, *imgdata = NULL;
431         int ret, channel, len;
432         long size;
433
434         ret = g3_cwd_command (camera->port, folder);
435         if (ret < GP_OK) goto out;
436
437         ret = gp_file_get_name (file, &fn);
438         if (ret < GP_OK) goto out;
439         ret = gp_file_get_data_and_size (file, &imgdata, &size);
440         if (ret < GP_OK) goto out;
441
442         cmd = malloc(strlen("-STOR ") + 20 + strlen(fn) + 2 + 1);
443         sprintf(cmd,"-STOR %ld %s", size, fn);
444         ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
445         free(cmd);
446         if (ret < GP_OK) goto out;
447         if (buf[0] != '1') { /* error, most likely file not found */
448                 ret = GP_ERROR;
449                 goto out;
450         }
451         ret = g3_channel_write(camera->port, 2, (char*)imgdata, size); /* data */
452         if (ret < GP_OK) goto out;
453         ret = g3_channel_read(camera->port, &channel, &reply, &len); /* reply */
454         if (ret < GP_OK) goto out;
455 out:
456         if (buf) free(buf);
457         if (reply) free(reply);
458         return (GP_OK);
459 }
460 #endif
461
462 static int
463 delete_file_func (CameraFilesystem *fs, const char *folder,
464                   const char *filename, void *data, GPContext *context)
465 {
466         Camera *camera = data;
467         char *reply = NULL, *cmd = NULL;
468         int ret;
469
470         ret = g3_cwd_command (camera->port, folder);
471         if (ret < GP_OK)
472                 return ret;
473
474         cmd = malloc(strlen("DELE ")+strlen(filename)+1);
475         if (!cmd) return GP_ERROR_NO_MEMORY;
476         sprintf(cmd,"DELE %s",filename);
477         ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
478         if (ret < GP_OK)
479                 goto out;
480         if (reply[0] == '5') {
481                 gp_context_error (context, _("Could not delete file."));
482                 ret = GP_ERROR;
483         }
484 out:
485         if (cmd) free(cmd);
486         if (reply) free(reply);
487         return (GP_OK);
488 }
489
490 static int
491 rmdir_func (CameraFilesystem *fs, const char *folder,
492                   const char *name, void *data, GPContext *context)
493 {
494         Camera *camera = data;
495         char *reply = NULL, *cmd = NULL;
496         int ret;
497
498         ret = g3_cwd_command (camera->port, folder);
499         if (ret<GP_OK)
500                 return ret;
501
502         cmd = realloc(cmd,strlen("RMD ")+strlen(name)+1);
503         if (!cmd) return GP_ERROR_NO_MEMORY;
504         sprintf(cmd,"RMD %s",name);
505         ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
506         if (ret < GP_OK)
507                 goto out;
508         if (reply[0] == '5') {
509                 gp_context_error (context, _("Could not remove directory."));
510                 ret = GP_ERROR;
511         }
512 out:
513         if (cmd) free(cmd);
514         if (reply) free(reply);
515         return (GP_OK);
516 }
517
518 static int
519 mkdir_func (CameraFilesystem *fs, const char *folder,
520           const char *name, void *data, GPContext *context)
521 {
522         Camera *camera = data;
523         char *reply = NULL, *cmd = NULL;
524         int ret;
525
526         ret = g3_cwd_command (camera->port, folder);
527         if (ret<GP_OK)
528                 return ret;
529
530         cmd = realloc(cmd,strlen("MKD ")+strlen(name)+1);
531         if (!cmd) return GP_ERROR_NO_MEMORY;
532         sprintf(cmd,"MKD %s",name);
533         ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
534         if (ret < GP_OK)
535                 goto out;
536         if (reply[0] == '5') {
537                 gp_context_error (context, _("Could not create directory."));
538                 ret = GP_ERROR;
539         }
540 out:
541         if (cmd) free(cmd);
542         if (reply) free(reply);
543         return (GP_OK);
544 }
545
546 static int
547 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
548 {
549         char *t = summary->text;
550         int ret;
551         char *buf = NULL;
552
553         t[0]='\0';
554         ret = g3_ftp_command_and_reply (camera->port, "-VER", &buf);
555         if (ret == GP_OK)
556                 sprintf(t+strlen(t), _("Version: %s\n"), buf+4); /* skip "200 " */
557         ret = g3_ftp_command_and_reply(camera->port, "-RTST", &buf); /* RTC test */
558         if (ret == GP_OK) {
559                 int rtcstat;
560                 if (sscanf(buf, "200 RTC status=%d", &rtcstat))
561                         sprintf(t+strlen(t), _("RTC Status: %d\n"), rtcstat);
562         }
563         ret = g3_ftp_command_and_reply(camera->port, "-TIME", &buf);
564         if (ret == GP_OK) {
565                 char day[20], time[20];
566                 if (sscanf(buf, "200 %s %s for -TIME", day, time))
567                         sprintf(t+strlen(t), _("Camera time: %s %s\n"), day, time);
568         }
569         ret = g3_ftp_command_and_reply(camera->port, "-GCID", &buf);
570         if (ret == GP_OK) {
571                 char camid[40];
572                 if (sscanf(buf, "200 CameraID=%s for -GCID", camid))
573                         sprintf(t+strlen(t), _("Camera ID: %s\n"), camid);
574         }
575         ret = g3_ftp_command_and_reply(camera->port, "-GSID", &buf);
576         if (ret == GP_OK) {
577                 char cardid[40];
578                 if (strstr(buf, "200 SD ID= for -GSID")) {
579                         sprintf(t+strlen(t), _("No SD Card inserted.\n"));
580                 } else {
581                         if (sscanf(buf, "200 SD ID=%s for -GSID", cardid)) {
582                                 sprintf(t+strlen(t), _("SD Card ID: %s\n"), cardid);
583                         }
584                 }
585         }
586         ret = g3_ftp_command_and_reply(camera->port, "-GTPN", &buf);
587         if (ret == GP_OK) {
588                 int total;
589                 if (sscanf (buf, "200 TotalPhotoNo=%d for -GTPN", &total))
590                         sprintf(t+strlen(t), _("Photos on camera: %d\n"), total);
591         }
592         ret = g3_ftp_command_and_reply(camera->port, "-DCAP /EXT0", &buf);
593         if (ret == GP_OK) {
594                 int space, sfree;
595                 if (sscanf (buf, "200 /EXT0 capacity %d byte,free %d byte.", &space, &sfree)) {
596                         sprintf(t+strlen(t), _("SD memory: %d MB total, %d MB free.\n"), space/1024/1024, sfree/1024/1024);
597                 }
598         }
599         ret = g3_ftp_command_and_reply(camera->port, "-DCAP /IROM", &buf);
600         if (ret == GP_OK) {
601                 int space, sfree;
602                 if (sscanf (buf, "200 /IROM capacity %d byte,free %d byte.", &space, &sfree)) {
603                         sprintf(t+strlen(t), _("Internal memory: %d MB total, %d MB free.\n"), space/1024/1024, sfree/1024/1024);
604                 }
605         }
606         if (buf) free (buf);
607         return (GP_OK);
608 }
609
610 static int
611 camera_about (Camera *camera, CameraText *about, GPContext *context)
612 {
613         strcpy (about->text, _("Ricoh Caplio G3.\n"
614                                "Marcus Meissner <marcus@jet.franken.de>\n"
615                                "Reverse engineered using USB Snoopy, looking\n"
616                                "at the firmware update image and wild guessing.\n"
617                                 ));
618
619         return (GP_OK);
620 }
621
622 static int
623 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
624                CameraFileInfo *info, void *data, GPContext *context)
625 {
626         Camera *camera = data;
627         int ret, bytes, width, height, k;
628         struct tm xtm;
629         char *cmd = NULL, *reply = NULL;
630
631         info->file.fields = GP_FILE_INFO_TYPE | GP_FILE_INFO_NAME |
632                         GP_FILE_INFO_SIZE | GP_FILE_INFO_TYPE;
633         strcpy(info->file.type,GP_MIME_UNKNOWN);
634         strcpy(info->file.name, filename);
635
636         if (!strcmp(filename+9,"JPG") || !strcmp(filename+9,"jpg"))
637                 strcpy(info->file.type,GP_MIME_JPEG);
638
639         if (!strcmp(filename+9,"AVI") || !strcmp(filename+9,"avi"))
640                 strcpy(info->file.type,GP_MIME_AVI);
641
642         if (!strcmp(filename+9,"WAV") || !strcmp(filename+9,"wav"))
643                 strcpy(info->file.type,GP_MIME_WAV);
644
645         if (!strcmp(filename+9,"MTA") || !strcmp(filename+9,"mta"))
646                 strcpy(info->file.type,"text/plain");
647
648         cmd = malloc(strlen("-FDAT ")+strlen(folder)+1+strlen(filename)+1);
649         sprintf(cmd, "-FDAT %s/%s", folder,filename);
650         ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
651         if (ret < GP_OK) goto out;
652         if (sscanf(reply, "200 date=%d:%d:%d %d:%d:%d",
653                         &xtm.tm_year,
654                         &xtm.tm_mon,
655                         &xtm.tm_mday,
656                         &xtm.tm_hour,
657                         &xtm.tm_min,
658                         &xtm.tm_sec
659         )) {
660                 xtm.tm_mon--;           /* range 0 - 11 */
661                 xtm.tm_year -= 1900;    /* number of years since 1900 */
662                 info->file.mtime = mktime(&xtm);
663                 info->file.fields |= GP_FILE_INFO_MTIME;
664         }
665
666         /* -INFO command only sometimes work on non jpeg/avi files */
667         if (    !strcmp(info->file.type,GP_MIME_JPEG) ||
668                 !strcmp(info->file.type,GP_MIME_AVI)
669         ) {
670                 sprintf(cmd, "-INFO %s/%s", folder,filename);
671                 ret = g3_ftp_command_and_reply(camera->port, cmd, &reply);
672                 if (ret < GP_OK) goto out;
673
674                 /* 200 1330313 byte W=2048 H=1536 K=0 for -INFO */
675                 if (sscanf(reply, "200 %d byte W=%d H=%d K=%d for -INFO", &bytes, &width, &height , &k)) {
676                         if (width != 0 && height != 0) {
677                                 info->file.fields |= GP_FILE_INFO_WIDTH | GP_FILE_INFO_HEIGHT;
678                                 info->file.height = height;
679                                 info->file.width = width;
680                         }
681                         info->file.fields |= GP_FILE_INFO_SIZE;
682                         info->file.size = bytes;
683                         if (k != 0)
684                                 gp_log(GP_LOG_ERROR, "g3", "k is %d for %s/%s\n", k, folder,filename);
685                 }
686         }
687 out:
688         if (reply) free(reply);
689         if (cmd) free(cmd);
690
691         return (GP_OK);
692
693 }
694
695
696 static int
697 folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
698                   void *data, GPContext *context)
699 {
700         Camera *camera = data;
701         char *buf = NULL, *reply = NULL;
702         char *cmd = NULL;
703         int ret = GP_OK, channel, len, rlen;
704
705         if (!strcmp("/",folder)) {
706                 /* Lets hope they dont invent other names. */
707
708                 /* We check the folder we get, since we should
709                  * not add EXT0 without SD card.
710                  */
711                 ret = g3_ftp_command_and_reply(camera->port, "-NLST /", &buf);
712                 if (ret < GP_OK) goto out;
713                 if (buf[0] == '4') {
714                         /* seen with R3: 450 Open Error... likely OK. */
715                         goto out;
716                 }
717                 if (buf[0] != '1') {
718                         ret = GP_ERROR_IO;
719                         goto out;
720                 }
721                 ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
722                 if (ret < GP_OK) goto out;
723                 ret = g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply  */
724                 if (ret < GP_OK) goto out;
725                 gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
726
727                 if (!strcmp("/EXT0",buf))
728                         gp_list_append (list, "EXT0", NULL);
729                 
730                 /* always add internal memory */
731                 gp_list_append (list, "IROM", NULL);
732                 return GP_OK;
733         }
734
735         cmd = malloc(6 + strlen(folder) + 1);
736         strcpy(cmd, "-NLST ");
737         strcat(cmd, folder);
738
739         ret = g3_ftp_command_and_reply(camera->port, cmd, &buf);
740         free(cmd);cmd = NULL;
741         if (ret < GP_OK) goto out;
742         if (buf[0] == '1') { /* 1xx means another reply follows */
743                 int n = 0;
744
745                 ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
746                 if (ret < GP_OK) goto out;
747                 g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply  */
748                 if (ret < GP_OK) goto out;
749                 gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
750
751                 for (n=0;n<len/32;n++) {
752                         if (buf[n*32+11] == 0x10) {
753                                 if (buf[n*32] == '.') /* skip . and .. entries */
754                                         continue;
755                                 ret = gp_list_append (list, buf+n*32, NULL);
756                                 if (ret != GP_OK) goto out;
757                         }
758                 }
759         } else {
760                 if (buf[0] == '4')
761                         ret = GP_OK;
762                 else    
763                         ret = GP_ERROR_IO;
764         }
765 out:
766         if (buf) free(buf);
767         if (reply) free(reply);
768         return ret;
769 }
770
771 /* for DOS FAT -> UNIX time conversion */
772 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
773
774 static int
775 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
776                 void *data, GPContext *context)
777 {
778         Camera *camera = data;
779         char *buf = NULL, *reply = NULL, *cmd;
780         int ret = GP_OK;
781         unsigned char *ubuf;
782
783         cmd = malloc(6 + strlen(folder) + 1);
784         strcpy(cmd, "-NLST ");
785         strcat(cmd, folder);
786         ret = g3_ftp_command_and_reply(camera->port, cmd, (char**)&buf);
787         free(cmd); cmd = NULL;
788         if (ret < GP_OK) goto out;
789         if (buf[0] == '1') { /* 1xx means another reply follows */
790                 int n = 0, channel, len, rlen;
791                 ret = g3_channel_read(camera->port, &channel, &buf, &len); /* data. */
792                 if (ret < GP_OK) goto out;
793                 g3_channel_read(camera->port, &channel, &reply, &rlen); /* next reply  */
794                 if (ret < GP_OK) goto out;
795                 gp_log(GP_LOG_DEBUG, "g3" , "reply %s", reply);
796
797                 ubuf = (unsigned char*)buf;
798                 for (n=0;n < len/32;n++) {
799                         if (buf[n*32+11] == 0x20) {
800                                 CameraFileInfo  info;
801                                 char xfn[13];
802                                 int date, time, month, year;
803
804                                 strncpy (xfn, buf+n*32, 8);
805                                 xfn[8] = '.';
806                                 strncpy (xfn+9, buf+n*32+8, 3);
807                                 xfn[8+1+3] = '\0';
808
809                                 ret = gp_filesystem_append (fs, folder, xfn, context);
810                                 if (ret < GP_OK) goto out;
811
812                                 /* we also get parts of fs info for free, so just set it */
813                                 info.file.fields =
814                                                 GP_FILE_INFO_NAME |
815                                                 GP_FILE_INFO_SIZE |
816                                                 GP_FILE_INFO_MTIME;
817                                 info.file.size =(ubuf[n*32+28]<<24)|
818                                                 (ubuf[n*32+29]<<16)|
819                                                 (ubuf[n*32+30]<< 8)|
820                                                 (ubuf[n*32+31]    );
821                                 strcpy(info.file.name,xfn);
822                                 if (!strcmp(xfn+9,"JPG") || !strcmp(xfn+9,"jpg")) {
823                                         strcpy(info.file.type,GP_MIME_JPEG);
824                                         info.file.fields |= GP_FILE_INFO_TYPE;
825                                 }
826
827                                 if (!strcmp(xfn+9,"AVI") || !strcmp(xfn+9,"avi")) {
828                                         strcpy(info.file.type,GP_MIME_AVI);
829                                         info.file.fields |= GP_FILE_INFO_TYPE;
830                                 }
831
832                                 if (!strcmp(xfn+9,"WAV") || !strcmp(xfn+9,"wav")) {
833                                         strcpy(info.file.type,GP_MIME_WAV);
834                                         info.file.fields |= GP_FILE_INFO_TYPE;
835                                 }
836
837                                 if (!strcmp(xfn+9,"MTA") || !strcmp(xfn+9,"mta")) {
838                                         strcpy(info.file.type,"text/plain");
839                                         info.file.fields |= GP_FILE_INFO_TYPE;
840                                 }
841                                 info.preview.fields = 0;
842                                 date = (ubuf[n*32+16]) | (ubuf[n*32+17]<<8);
843                                 time = (ubuf[n*32+14]) | (ubuf[n*32+15]<<8);
844
845                                 /* from kernel fs/fat/, time_dos2unix. */
846                                 month = ((date >> 5) - 1) & 15;
847                                 year = date >> 9;
848                                 info.file.mtime =
849                                         (time & 31)*2+60*((time >> 5) & 63)+
850                                         (time >> 11)*3600+86400*((date & 31)-1+
851                                         day_n[month]+(year/4)+year*365-
852                                         ((year & 3) == 0 && month < 2 ? 1 : 0)+
853                                         3653);
854
855                                 ret = gp_filesystem_set_info_noop(fs, folder, info, context);
856  
857                         }
858                 }
859         } else {
860                 if (buf[0] == '4') /* 450 Open Error ... like dir not there */
861                         ret = GP_OK;
862                 else            
863                         ret = GP_ERROR_IO;
864         }
865 out:
866         if (buf) free(buf);
867         if (reply) free(reply);
868         return (GP_OK);
869 }
870
871 static CameraFilesystemFuncs fsfuncs = {
872         .file_list_func = file_list_func,
873         .get_file_func = get_file_func,
874         .del_file_func = delete_file_func,
875         .get_info_func = get_info_func,
876         .folder_list_func = folder_list_func,
877         .make_dir_func = mkdir_func,
878         .remove_dir_func = rmdir_func,
879 };
880
881 int
882 camera_init (Camera *camera, GPContext *context) 
883 {
884         /*char *buf;*/
885         GPPortSettings settings;
886
887         /* First, set up all the needed function pointers */
888         camera->functions->summary              = camera_summary;
889         camera->functions->about                = camera_about;
890
891         /* Now, tell the filesystem where to get lists, files and info */
892         gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
893
894         gp_port_get_settings(camera->port, &settings);
895         settings.usb.inep = 0x81;
896         settings.usb.outep = 0x02;
897         settings.usb.intep = 0x83;
898         gp_port_set_settings(camera->port, settings);
899         /*
900          * The port is already provided with camera->port (and
901          * already open). You just have to use functions like
902          * gp_port_timeout_set, gp_port_settings_get, gp_port_settings_set.
903          */
904         
905         /*
906          * Once you have configured the port, you should check if a 
907          * connection to the camera can be established.
908          */
909
910         /* testing code ... 
911         buf = NULL;
912         g3_ftp_command_and_reply(camera->port, "-PWOF STDBY", &buf);
913         */
914         return (GP_OK);
915 }